滑块(在自定义类中创建)不会移动或更新

时间:2019-03-16 10:45:25

标签: flutter slider

我刚刚开始使用flutter进行android应用程序开发,并且我正在学习。

有关当前项目的详细信息: 我正在创建一个骰子模拟器,用户可以在其中点击绘制的骰子来掷骰子。我增加了震动效果,吐司也。现在,我想让用户使用滑块修改骰子的大小。

我在文档中找到了slider class。 我还在stackoverflow中咨询了this线程。不过,我仍然无法完全理解这个概念。我将不胜感激。

问题:

所说的滑块确实可以渲染它的样子,但是我似乎无法拖动它。

main.dart

import 'package:flutter/material.dart';
import 'package:do_dice/dice2D.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';

void showToastText(int a) {
  Fluttertoast
      .cancel(); //to clear all scheduled toast call ; this is to make the toast cal seem instant responsive.
  Fluttertoast.showToast(
    msg: '$a',
    toastLength: Toast.LENGTH_SHORT,
    gravity: ToastGravity.BOTTOM,
    backgroundColor: Colors.black87,
    textColor: Colors.yellow,
    fontSize: 14.0,
  );
}

void main() {
  SystemChrome.setSystemUIOverlayStyle(
    SystemUiOverlayStyle(
      statusBarColor: Color.fromRGBO(0, 0, 0, 0.0), //status bar is transparent
    ),
  );
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ROLL THE DICE',
      theme: ThemeData(
        brightness: Brightness.dark,
      ),
      home: MyHomePage(title: 'ROLL THE DICE'),
      debugShowCheckedModeBanner: false,
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Dice2D dice1 = new Dice2D(
    size: 300.0,
    borderWidth: 5.0,
    displayInt: 2,
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Center(
            child: Text(
          widget.title,
        )),
      ),
      body: Column(
        children: <Widget>[
          SliderDiceSize(
            minValue: 100.0,
            maxValue: 300.0,
            title: "Set the size of dice:",
            dice: dice1,
            titleColor: Colors.yellow,
          ),
          SliderBorderWidth(titleColor: Colors.yellow,title: "Set the border width of dice:",dice: dice1,minValue: 1.0,maxValue: 10.0,),
          Expanded(child: Center(child: dice1)),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          //function to display the number on dice as a toast
          showToastText(dice1.getDisplay());
        },
        backgroundColor: Colors.yellow,
        tooltip: "Show the number.",
        child: Icon(Icons.message),
      ),
    );
  }
}

dice2D.dart

    import 'package:flutter/material.dart';
import 'dart:math';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:vibration/vibration.dart';

void vibrateDiceRolling() {
  Vibration.cancel();
  Vibration.vibrate(duration: 50); //default is 500 ms
}

void showToastRolling() {
  Fluttertoast
      .cancel(); //to clear all scheduled toast call ; this is to make the toast call seem instant responsive.
  Fluttertoast.showToast(
    msg: "Roger that!",
    toastLength: Toast.LENGTH_SHORT,
    gravity: ToastGravity.BOTTOM,
    backgroundColor: Colors.black87,
    textColor: Colors.white70,
    fontSize: 14.0,
  );
}

class paintDice2D extends StatelessWidget {
  @override
  @required
  final int display;
  @required
  final double borderWidth;
  @required
  final Color diceColor;
  @required
  final double diceSize;

  paintDice2D(
      {Key key, this.diceSize, this.display, this.borderWidth, this.diceColor})
      : super(key: key);

  Widget drawEmptyBox() {
    return Center(
      child: Container(),
    );
  }

  Widget drawCircleDot() {
    return Center(
      child: Container(
        decoration: BoxDecoration(shape: BoxShape.circle, color: diceColor),
      ),
    );
  }

  Widget build(BuildContext context) {
    double divSize = (diceSize - 2 * borderWidth) / 3;
    return Container(
      decoration: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(10.0)),
        border: Border.all(
            color: diceColor, width: borderWidth, style: BorderStyle.solid),
      ),
      height: diceSize,
      width: diceSize,
      child: Row(
        children: <Widget>[
          Column(
            children: <Widget>[
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child: (display == 3 ||
                            display == 4 ||
                            display == 5 ||
                            display == 6) ==
                        true
                    ? drawCircleDot()
                    : drawEmptyBox(), // condition == true ?  {code for true } : {code for false}
              ),
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child:
                    (display == 6) == true ? drawCircleDot() : drawEmptyBox(),
              ),
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child: (display == 2 ||
                            display == 4 ||
                            display == 5 ||
                            display == 6) ==
                        true
                    ? drawCircleDot()
                    : drawEmptyBox(),
              ),
            ],
          ),
          Column(
            children: <Widget>[
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child: drawEmptyBox(),
              ),
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child: (display == 1 || display == 3 || display == 5) == true
                    ? drawCircleDot()
                    : drawEmptyBox(),
              ),
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child: drawEmptyBox(),
              ),
            ],
          ),
          Column(
            children: <Widget>[
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child: (display == 2 ||
                            display == 4 ||
                            display == 5 ||
                            display == 6) ==
                        true
                    ? drawCircleDot()
                    : drawEmptyBox(),
              ),
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child:
                    (display == 6) == true ? drawCircleDot() : drawEmptyBox(),
              ),
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child: (display == 3 ||
                            display == 4 ||
                            display == 5 ||
                            display == 6) ==
                        true
                    ? drawCircleDot()
                    : drawEmptyBox(),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

class Dice2D extends StatefulWidget {
  @override
  @required
  double size;
  @required
  double borderWidth;
  @required
  int displayInt;

  Dice2D({
    Key key,
    this.size,
    this.borderWidth,
    this.displayInt,
  }) : super(key: key);

  int getDisplay() {
    return this.displayInt;
  }

  Dice2DState createState() {
    return new Dice2DState();
  }
}

class Dice2DState extends State<Dice2D> {
  @override
  Widget build(BuildContext context) {
    int nextDisplay = Random().nextInt(6) + 1;
        void rollDice() {
            setState(() {
                widget.displayInt = nextDisplay;
                nextDisplay = Random().nextInt(6) + 1;
                showToastRolling();
                vibrateDiceRolling();
            });
        }

        return FlatButton(
            onPressed: () {
                rollDice();
            },
            padding: EdgeInsets.all(0.0),
            child: paintDice2D(
                display: widget.displayInt,
                borderWidth: widget.borderWidth,
                diceSize: widget.size,
                diceColor: Colors.yellow,
            ),
        );
    }
  }


class SliderDiceSize extends StatefulWidget {
  @required final String title;
  @required Dice2D dice;
  @required final double minValue;
  @required final double maxValue;
  @required final Color titleColor;

  SliderDiceSize({Key key, this.title,  this.dice, this.titleColor, this.maxValue, this.minValue}):super(key:key);

  @override
  SliderDiceSizeState createState() {
    return new SliderDiceSizeState();
  }
}

class SliderDiceSizeState extends State<SliderDiceSize> {

  void setDiceSize(double a) {
    setState(() {
      int diceint = widget.dice.getDisplay(); //**
      widget.dice.displayInt = diceint; //**
      widget.dice.size = a; //**
    });
  }

  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: new Text(
            widget.title,
            style: TextStyle(color: widget.titleColor, fontSize: 16.0),
          ),
        ),
        Container(
          child: new Slider(
            value: widget.dice.size, //**
            onChanged: (double value) => setDiceSize(value), //**
            max: widget.maxValue,
            min: widget.minValue,
            activeColor: Colors.grey,
            inactiveColor: Colors.white12,
          ),
        ),
      ],
    );
  }
}

class SliderBorderWidth extends StatefulWidget {
  @required
  final String title;
  @required
  Dice2D dice;
  @required final double minValue;
  @required final double maxValue;
  @required final Color titleColor;

  SliderBorderWidth(
      {Key key,
      this.dice,
      this.title,
      this.minValue,
      this.maxValue,
      this.titleColor});

  @override
  SliderBorderWidthState createState() {
    return new SliderBorderWidthState();
  }
}

class SliderBorderWidthState extends State<SliderBorderWidth> {
    void setBorderWidth(double a) {
    setState(() {
        int diceint = widget.dice.getDisplay();  //**
        widget.dice.borderWidth = a;  //**
    });}

  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: new Text(
          widget.title,
            style: TextStyle(color: widget.titleColor, fontSize: 16.0),
          ),
        ),
        Container(
          child: new Slider(
            value: widget.dice.borderWidth,  //**
            onChanged: (double value) => setBorderWidth(value),  //**
            max: widget.maxValue,
            min: widget.minValue,
            activeColor: Colors.grey,
            inactiveColor: Colors.white12,
          ),
        ),
      ],
    );
  }
}

编辑

下面是使用回调函数的新完整代码。我已经用~~标记了更新的代码行。 滑块无法拖动的较早问题再次弹出。我对引起问题的行的怀疑在代码中用注释标记。

main.dart

导入“ package:flutter / material.dart”;

import 'package:do_dice/dice2D.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';

void showToastText(int a) {
  Fluttertoast
      .cancel(); //to clear all scheduled toast call ; this is to make the toast cal seem instant responsive.
  Fluttertoast.showToast(
    msg: '$a',
    toastLength: Toast.LENGTH_SHORT,
    gravity: ToastGravity.BOTTOM,
    backgroundColor: Colors.black87,
    textColor: Colors.yellow,
    fontSize: 14.0,
  );
}

void main() {
  SystemChrome.setSystemUIOverlayStyle(
    SystemUiOverlayStyle(
      statusBarColor: Color.fromRGBO(0, 0, 0, 0.0), //status bar is transparent
    ),
  );
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ROLL THE DICE',
      theme: ThemeData(
        brightness: Brightness.dark,
      ),
      home: MyHomePage(title: 'ROLL THE DICE'),
      debugShowCheckedModeBanner: false,
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Dice2D dice1 = new Dice2D(
    size: 300.0,
    borderWidth: 5.0,
    displayInt: 2,
  );

//~~ added this function to serve as callback
updateDiceSize(Dice2D dice,double size){
    setState((){  
        dice.size = size;
    });
 }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Center(
            child: Text(
          widget.title,
        )),
      ),
      body: Column(
        children: <Widget>[
          SliderDiceSize(updateDiceSizeCallback: updateDiceSize(dice1,dice1.size),  //~~passing the callback
            minValue: 100.0,
            maxValue: 300.0,
            title: "Set the size of dice:",
            dice: dice1,
            titleColor: Colors.yellow,
          ),
          Expanded(child: Center(child: dice1)),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          //function to display the number on dice as a toast
          showToastText(dice1.getDisplay());
        },
        backgroundColor: Colors.yellow,
        tooltip: "Show the number.",
        child: Icon(Icons.message),
      ),
    );
  }
}

Dice2D.dart

import 'package:flutter/material.dart';
import 'dart:math';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:vibration/vibration.dart';

void vibrateDiceRolling() {
  Vibration.cancel();
  Vibration.vibrate(duration: 50); //default is 500 ms
}

void showToastRolling() {
  Fluttertoast
      .cancel(); //to clear all scheduled toast call ; this is to make the toast call seem instant responsive.
  Fluttertoast.showToast(
    msg: "Roger that!",
    toastLength: Toast.LENGTH_SHORT,
    gravity: ToastGravity.BOTTOM,
    backgroundColor: Colors.black87,
    textColor: Colors.white70,
    fontSize: 14.0,
  );
}

class PaintDice2D extends StatelessWidget {
  @override
  @required
  final int display;
  @required
  final double borderWidth;
  @required
  final Color diceColor;
  @required
  final double diceSize;

  PaintDice2D(
      {Key key, this.diceSize, this.display, this.borderWidth, this.diceColor})
      : super(key: key);

  Widget drawEmptyBox() {
    return Center(
      child: Container(),
    );
  }

  Widget drawCircleDot() {
    return Center(
      child: Container(
        decoration: BoxDecoration(shape: BoxShape.circle, color: diceColor),
      ),
    );
  }

  Widget build(BuildContext context) {
    double divSize = (diceSize - 2 * borderWidth) / 3;
    return Container(
      decoration: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(10.0)),
        border: Border.all(
            color: diceColor, width: borderWidth, style: BorderStyle.solid),
      ),
      height: diceSize,
      width: diceSize,
      child: Row(
        children: <Widget>[
          Column(
            children: <Widget>[
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child: (display == 3 ||
                            display == 4 ||
                            display == 5 ||
                            display == 6) ==
                        true
                    ? drawCircleDot()
                    : drawEmptyBox(), // condition == true ?  {code for true } : {code for false}
              ),
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child:
                    (display == 6) == true ? drawCircleDot() : drawEmptyBox(),
              ),
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child: (display == 2 ||
                            display == 4 ||
                            display == 5 ||
                            display == 6) ==
                        true
                    ? drawCircleDot()
                    : drawEmptyBox(),
              ),
            ],
          ),
          Column(
            children: <Widget>[
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child: drawEmptyBox(),
              ),
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child: (display == 1 || display == 3 || display == 5) == true
                    ? drawCircleDot()
                    : drawEmptyBox(),
              ),
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child: drawEmptyBox(),
              ),
            ],
          ),
          Column(
            children: <Widget>[
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child: (display == 2 ||
                            display == 4 ||
                            display == 5 ||
                            display == 6) ==
                        true
                    ? drawCircleDot()
                    : drawEmptyBox(),
              ),
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child:
                    (display == 6) == true ? drawCircleDot() : drawEmptyBox(),
              ),
              Container(
                width: divSize,
                height: divSize,
                padding: EdgeInsets.all(divSize / 5),
                child: (display == 3 ||
                            display == 4 ||
                            display == 5 ||
                            display == 6) ==
                        true
                    ? drawCircleDot()
                    : drawEmptyBox(),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

class Dice2D extends StatefulWidget {
  @override
  @required
  double size;
  @required
  double borderWidth;
  @required
  int displayInt;

  Dice2D({
    Key key,
    this.size,
    this.borderWidth,
    this.displayInt,
  }) : super(key: key);

  int getDisplay() {
    return this.displayInt;
  }

  Dice2DState createState() {
    return new Dice2DState();
  }
}

class Dice2DState extends State<Dice2D> {
  @override
  Widget build(BuildContext context) {
    int nextDisplay = Random().nextInt(6) + 1;
        void rollDice() {
            setState(() {
                widget.displayInt = nextDisplay;
                nextDisplay = Random().nextInt(6) + 1;
                showToastRolling();
                vibrateDiceRolling();
            });
        }

        return FlatButton(
            onPressed: () {
                rollDice();
            },
            padding: EdgeInsets.all(0.0),
            child: PaintDice2D(
                display: widget.displayInt,
                borderWidth: widget.borderWidth,
                diceSize: widget.size,
                diceColor: Colors.yellow,
            ),
        );
    }
  }


class SliderDiceSize extends StatefulWidget {
  @required final String title;
  @required Dice2D dice;
  @required final double minValue;
  @required final double maxValue;
  @required final Color titleColor;
  @required Function updateDiceSizeCallback;  //~~
  SliderDiceSize({Key key, this.title,  this.dice, this.titleColor, this.maxValue, this.minValue,this.updateDiceSizeCallback}):super(key:key);  //~~

  @override
  SliderDiceSizeState createState() {
    return new SliderDiceSizeState();
  }
}

class SliderDiceSizeState extends State<SliderDiceSize> {
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: new Text(
            widget.title,
            style: TextStyle(color: widget.titleColor, fontSize: 16.0),
          ),
        ),
        Container(
          child: new Slider(
            value: widget.dice.size,
            onChanged: (double value) => widget.updateDiceSizeCallback, //~~ I believe something needs to change here for slider to be able to drag.
            max: widget.maxValue,
            min: widget.minValue,
            activeColor: Colors.grey,
            inactiveColor: Colors.white12,
          ),
        ),
      ],
    );
  }
}

因为这是我的第一个问题,对于代码和变量名的混乱,我深表歉意。

1 个答案:

答案 0 :(得分:0)

您正在保存和使用小部件内部的属性,而不是状态类。 据我了解,widget中的StatefulWidget就像StatelessWidget。实际的state应该在State<>创建的StatefulWidget类中。

尝试将骰子(Dice2D)的属性添加到实际状态类中。

[编辑]

好的,所以我仔细研究了您的代码,我认为您没有更新正确的State。 您的滑块仅更新自己的StatesetState()就是这样做的)。但是他们应该更新其父母State_MyHomePageState),因为这将触发MyHomePageWidget用新的State进行重建。 您的Die在单击时正在正确重建,因为这会触发Dice2DState更新其自身状态,这确实会触发Dice2D小部件的重建。

这就是问题所在。现在如何解决这个问题。您需要一种在setState()上触发_MyHomePageState的方法。有不同的方法可以做到这一点。但是因为您只是尝试一些东西,所以我建议您使用回调。

在您的_MyHomePageState中,添加以下功能:

// Could also be separate functions for each property
updateDice({double size, double borderWidth, int displayInt}) {
    setState(() {
     if(size != null) dice1.size = size;
     if(borderWidth != null) dice1.borderWidth = borderWidth;
     if(displayInt != null) dice1.displayInt = displayInt; 
    });
  }

这将用于更新Dice

您的滑块将不得不使用此功能,因此请将此函数作为回调传递给滑块的构造函数: SliderBorderWidth(updateDice ... rest of your params ...)

并将其存储在滑块小部件中: Function updateDiceCallback

more about passing functions here

现在,您的滑块可以使用此回调来更新骰子的状态。 因为现在正在对骰子的状态进行更高级别的管理,所以Dice Widget应该是StatelessWidget,因为无论如何它都会被重绘,并且只是一种视觉表示。 您还应该向其传递UpdateDice回调,以便可以将其连接到OnPressed事件或其他事件,并使其更新编号。

我希望这对您有意义。我试图修复您的代码,但我认为您需要了解状态更好地工作的方式。我将为您创建一个示例并将其添加。

[样本]

尝试一下,也许可以帮助您理解。刚刚将它放在新项目的main.dart中。

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:math';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ROLL THE DICE',
      theme: ThemeData(
        brightness: Brightness.dark,
      ),
      home: MyHomePage(title: 'ROLL THE DICE'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Dice2D dice1 = new Dice2D(
    size: 150.0,
    displayInt: 2,
  );

  updateDice({double size, int displayInt}) {
    setState(() {
     if(size != null) dice1.size = size;
     if(displayInt != null) dice1.displayInt = displayInt; 
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Center(
            child: Text(
          widget.title,
        )),
      ),
      body: Column(
        children: [
          Slider(
            value: dice1.size,
            min: 100.0,
            max: 200.0,
            onChanged: (value){
            updateDice(size: value);
          },),
          DiceWidget(dice1.size, dice1.displayInt, updateDice),
        ],
      ),
    );
  }
}

class DiceWidget extends StatelessWidget {

  final int _number;
  final double _size;
  final Function _onPressed;

  DiceWidget(this._size, this._number, this._onPressed);

  @override
  Widget build(BuildContext context) {
    return Center(child: RaisedButton(
      onPressed: (){
        _onPressed(displayInt: Random().nextInt(5)+1);
      },
      child: Text("$_number", style: TextStyle(fontSize: _size),)),);
  }
}

class Dice2D {
  var size;
  var displayInt;

  Dice2D({this.size,this.displayInt});
}
相关问题