首先让我说起我是Flutter / Dart的新手,而不是超级有经验的程序员。
我试图使自己熟悉flutter的框架和工具,并且试图扩展flutter在项目生成时创建的基本计数器应用程序。我的目标是让应用程序跟踪计数器“重置”的时间,保持计数器的时间和计数,然后在另一个屏幕上的表中显示该数据。
这是我到目前为止的内容: 我开设了一个班来跟踪数据:
class CounterRecord {
int _counter; //Holds the value the counter was at on reset
DateTime _resetTime; //Holds the time when the counter was reset
CounterRecord(int _count){
_counter = _count;
_resetTime = DateTime.now();
}
int getCount() => _counter; //fetch method for count
DateTime getTime() => _resetTime; //Fetch method for resettime
}
这是主要的班级/主页:
import 'package:counter_app/clickerScreen.dart';
import 'package:counter_app/dataScreen.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
final clickerKey = new GlobalKey<ClickerScreenState>();
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.deepOrange,
accentColor: Colors.grey,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home'),
);
}
}
class MyHomePage extends StatefulWidget {
//Enables the passing in of the title, clicker screen instance, and datacreen isntance, respectively,
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
//We don't want a brand new clickerScreen every time, so I'm keeping it up here.
ClickerScreen clickerScreen = ClickerScreen(clickerKey: clickerKey); //Creates a new clickerScreen - the key points to it too.
@override
Widget build(BuildContext context) {
//Creates an instance (State?) of clickerScreen for the first tab
return DefaultTabController( //A wrapper that helps manage the tab states
length: 2, //Currently there are only two options for screens
child: Scaffold(
appBar: AppBar( //This represnts the bar up at the top
title: Text(widget.title),
bottom: TabBar(
tabs: [
//These are the icons for the two tabs we're using
//The order of these is important: It goes in the same order as TabBarView below
Tab(icon: Icon(Icons.home)),
Tab(icon: Icon(Icons.directions_run)),
],
)
),
body: TabBarView(
children: [
clickerScreen,
DataScreen( //this DataScreen will be built every time based on the new data from clickerScreen
data: clickerKey.currentState.getRecords(),
),
],
),
),
);
}
}
class CounterRecord {
int _counter; //Holds the value the counter was at on reset
DateTime _resetTime; //Holds the time when the counter was reset
CounterRecord(int _count){
_counter = _count;
_resetTime = DateTime.now();
}
int getCount() => _counter; //fetch method for count
DateTime getTime() => _resetTime; //Fetch method for resettime
}
这是我的clickerScreen文件的重要部分:
class ClickerScreen extends StatefulWidget {
ClickerScreen({Key clickerKey}) : super(key: clickerKey);
@override
ClickerScreenState createState(){
return ClickerScreenState();
}
}
class ClickerScreenState extends State<ClickerScreen> {
int _counter = 0;
List<CounterRecord> records;
/* All three of these functions do very similar things, modify the counter value. */
void _resetCounter(){
setState(() {
records.add(CounterRecord(_counter));
_counter = 0;
});
}
List<CounterRecord> getRecords(){
return records;
}
clickerScreen中有一个构建方法,该方法仅显示按钮和文本。我没有在其中分配密钥,因为它只是返回一个中心小部件,但是我读了一些建议也许应该的东西。
这是我的dataScreen文件:
import 'package:flutter/material.dart';
import 'main.dart';
class DataScreen extends StatefulWidget{
//Enables the passing in of the instance of the clicker screen instance
DataScreen({Key key, @required this.data}) : super(key: key);
final List<CounterRecord> data;
@override
_DataScreenState createState(){
return _DataScreenState();
}
}
class _DataScreenState extends State<DataScreen>{
@override
Widget build(BuildContext context) {
return Text(widget.data.toString());
}
}
我知道它的显示实际上并不像它应该的那样,因为我只是将其发送给toString(),但我想确保在开始处理该数据之前可以传递数据。
运行此命令时,在getRecords()上收到NoSuchMethod错误,接收者:null。我也尝试过在ClickerScreen小部件上调用createState(),这是最后一次尝试。
有什么建议吗?
(我将整个clickerScreen文件粘贴在这里(https://pastebin.com/j6Y8M8F3),因为我不想再发布此帖子了。)
答案 0 :(得分:0)
如果您有两个取决于同一状态的小部件,则必须使用一种称为“提升状态”的东西。这意味着状态是最接近的窗口小部件的一部分,该窗口小部件具有其他两个窗口小部件作为子窗口。在您的情况下,这将是保存CounterRecord List的MyHomePage小部件。它将列表通过构造函数传递给DataScreen,并将onReset回调传递给ClickerScreen。
MyHomePage:
class MyHomePage extends StatefulWidget {
//Enables the passing in of the title, clicker screen instance, and datacreen isntance, respectively,
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<CounterRecord> counterRecord = []; //this is the lifted up state
onReset(int count) {
setState(() {
counterRecord.add(CounterRecord(count));
});
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
//A wrapper that helps manage the tab states
length: 2, //Currently there are only two options for screens
child: Scaffold(
appBar: AppBar(
//This represnts the bar up at the top
title: Text(widget.title),
bottom: TabBar(
tabs: [
//These are the icons for the two tabs we're using
//The order of these is important: It goes in the same order as TabBarView below
Tab(icon: Icon(Icons.home)),
Tab(icon: Icon(Icons.directions_run)),
],
)),
body: TabBarView(
children: [
ClickerScreen(onReset: onReset),
DataScreen(
data: counterRecord, //pass the record data to the datascreen
),
],
),
),
);
}
}
ClickerScreen:
class ClickerScreen extends StatefulWidget {
final Function(int) onReset;
ClickerScreen({Key clickerKey, this.onReset}) : super(key: clickerKey);
@override
ClickerScreenState createState() {
return ClickerScreenState();
}
}
class ClickerScreenState extends State<ClickerScreen> {
int _counter = 0;
/* All three of these functions do very similar things, modify the counter value. */
void _resetCounter() {
widget.onReset(_counter); //call the onReset callback with the counter
setState(() {
_counter = 0;
});
}
@override
Widget build(BuildContext context) {
....
}
}
DataScreen(可以是无状态的,因为状态在其父级中)
class DataScreen extends StatelessWidget{
//Enables the passing in of the instance of the clicker screen instance
DataScreen({Key key, @required this.data}) : super(key: key);
final List<CounterRecord> data;
@override
Widget build(BuildContext context) {
return Text(widget.data.toString());
}
}
使用这种简单方法会很快变得很烦人,并且在小部件树中移动小部件时需要进行大量更改。这就是为什么存在诸如具有ChangeNotifier或Bloc的Provider之类的高级状态管理的原因。
以下是关于此事的好读物::
https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple
答案 1 :(得分:0)
我听了利奥·莱托(Leo Letto)的建议,而是使用了一个InheritedWidget,该InheritedWidget放置在最上面,其中包含记录列表。