Слайдер (созданный в пользовательском классе) не перемещается или не обновляется - PullRequest
0 голосов
/ 16 марта 2019

Я только начал разработку приложений для Android, используя флаттер, и я учусь на этом.

Подробности о текущем проекте: Я создаю симулятор игры в кости, в котором пользователь может нажать на нарисованные кости, чтобы бросить кости. Я добавил эффект вибрации, тост тоже. Теперь я хочу позволить пользователю изменять размер кубика с помощью ползунка.

Я нашел класс ползунка в документации. Я также консультировался с этим потоком в stackoverflow. Тем не менее, я не могу полностью понять концепцию. Буду признателен за любую помощь.

Проблема:

Сказанный слайдер рендерится так, как он должен быть, но я не могу его перетащить.

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,
          ),
        ),
      ],
    );
  }
}

EDIT

Ниже приведен новый полный код с использованием функции обратного вызова. Я пометил обновленные строки кода, используя ~~. Ранее проблема ползунка, неспособного перетащить, снова возникла. Мои подозрения в отношении линий, вызывающих проблемы, отмечены комментарием в коде.

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,
  );

//~~ 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 голосов
/ 16 марта 2019

Вы сохраняете и используете свойства внутри виджета вместо класса состояний.Как я понимаю, widget из StatefulWidget точно так же, как StatelessWidget.Фактический state должен быть в классе State<>, который создает StatefulWidget.

Попробуйте добавить свойство для кости (Dice2D) в фактический класс состояния.

[править]

Хорошо, я внимательно посмотрел на ваш код и думаю, что вы не обновляете State.Ваши ползунки только обновляют свои State (что и делает setState()).Но они должны обновлять своих родителей 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

подробнее о прохождении функций здесь

Теперь ваши ползунки могут использовать этот обратный вызов для обновления состояния ваших костей.Поскольку состояние вашей игры в кости теперь управляется выше по иерархии, ваш виджет для игры в кости должен иметь значение StatelessWidget, потому что он все равно перерисовывается и является просто визуальным представлением.Вы также должны передать ему обратный вызов UpdateDice, чтобы связать его с событием OnPressed или чем-то другим и заставить его обновить номер.

Надеюсь, это имеет какой-то смысл для вас.Я пытался исправить ваш код, но я думаю, что вам нужно понять, как лучше работает состояние.Я создам для вас пример и добавлю его.

[sample]

Попробуйте, возможно, это поможет вам понять.Просто мимо этого в 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});
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...