我刚刚开始使用flutter进行android应用程序开发,并且我正在学习。
有关当前项目的详细信息: 我正在创建一个骰子模拟器,用户可以在其中点击绘制的骰子来掷骰子。我增加了震动效果,吐司也。现在,我想让用户使用滑块修改骰子的大小。
我在文档中找到了slider class。 我还在stackoverflow中咨询了this线程。不过,我仍然无法完全理解这个概念。我将不胜感激。
问题:
所说的滑块确实可以渲染它的样子,但是我似乎无法拖动它。
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),
),
);
}
}
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,
),
),
],
);
}
}
编辑
下面是使用回调函数的新完整代码。我已经用~~标记了更新的代码行。 滑块无法拖动的较早问题再次弹出。我对引起问题的行的怀疑在代码中用注释标记。
导入“ 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),
),
);
}
}
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,
),
),
],
);
}
}
因为这是我的第一个问题,对于代码和变量名的混乱,我深表歉意。
答案 0 :(得分:0)
您正在保存和使用小部件内部的属性,而不是状态类。
据我了解,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
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});
}