说一下背景,就是我们在编写 Flutter 程序的时候,我们目前是将所有的代码都编写在一个文件中,现在代码量比较少所以看上去还好,但是当代码量比较大的时候,这样的代码就会显得非常的臃肿,不利于我们的维护。
那么了解了这些背景内容之后,那么官方是如何解决这个问题的呢?
首先我们来看看官方给我们的示例代码,先将多余的注释代码给删除,然后再来看,通过观察可以发现官方是编写了一个 MyApp 类,继承了 StatelessWidget(组件类)。
在Flutter中,组件(Widgets)是构建用户界面的基本元素。组件分为两种类型:有状态组件(Stateful Widgets)和无状态组件(Stateless Widgets)。
示例代码:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Stateless Widget'),
),
body: Center(
child: Text('Hello, Flutter!'),
),
),
);
}
}
示例代码:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Stateful Widget'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
),
);
}
}
在我们之前的示例代码中,我们在 runApp
中是直接编写组件的,现在我们将组件单独抽离出来,然后在 runApp
中引用,通过继承 StatelessWidget
来实现无状态组件。
如下图是之前的代码:
现在我们将组件抽离出来,开始改造:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
// 应用程序的标题,显示在任务管理窗口中。
title: "my App",
// 应用程序的主题,用于定义颜色,字体和阴影等。接受一个 ThemeData 对象
theme: ThemeData(primarySwatch: Colors.blue),
// 应用的首页
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
title: const Text('首页'),
centerTitle: true,
),
body: const Center(
child: Text(
'Hello Flutter',
style: TextStyle(fontSize: 20.0),
),
)
)
);
}
}
运行结果:
如上代码,我们将组件抽离出来,然后在 runApp
中引用,通过继承 StatelessWidget
来实现无状态组件。
通过我的观察 MaterialApp 中的 home 属性是一个 Scaffold 组件,所以我们将 Scaffold 组件也抽离出来,然后在 MaterialApp 中引用。
class MyHome extends StatelessWidget {
const MyHome({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
title: const Text('首页'),
centerTitle: true,
),
body: const Center(
child: Text(
'Hello Flutter MyHome',
style: TextStyle(fontSize: 20.0),
),
)
);
}
}
运行结果:
body 中的内容是一个 Center 组件,所以我们将 Center 组件也抽离出来,然后在 Scaffold 中引用, 这里我就不浪费时间了因为本次还有一个有状态组件还要介绍无状态组件的使用就到这里了。
在深入了解有状态组件之前,先尝试利用无状态组件管理状态,看看能否实现预期效果。在此之前,也要提醒大家注意无状态组件中的一个重要事项。然后再深入了解有状态组件。
首先创建了一个名为 MyHomeTwo 的组件,其构建函数返回了一个 Center 组件。在 Center 组件的子组件中,使用了 Row 组件,并在 Row 的 children 属性中放置了一个 Checkbox。这样做是为了利用 Checkbox 的选中状态来测试是否能够修改状态。
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
// 应用程序的标题,显示在任务管理窗口中。
title: "my App",
// 应用程序的主题,用于定义颜色,字体和阴影等。接受一个 ThemeData 对象
theme: ThemeData(primarySwatch: Colors.blue),
// 应用的首页
home: const MyHome());
}
}
class MyHome extends StatelessWidget {
const MyHome({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
title: const Text('首页'),
centerTitle: true,
),
body: MyHomeTwo());
}
}
class MyHomeTwo extends StatelessWidget {
var isCheck = true;
@override
Widget build(BuildContext context) {
return Center(
child: Row(
children: [Checkbox(value: isCheck, onChanged: (val) {
print(val);
this.isCheck = val isbool;
print(this.isCheck);
})],
),
);
}
}
运行结果:
从效果图中可以看到我们的 isCheck 一直输出的是 true,我已经通过 this.isCheck = val is bool;
这个代码重新赋值了,但是还是没有改变,这是为什么呢?
这里需要强调无状态组件中的一个关键注意事项, 在无状态组件中,组件被创建之后会将组件中的变量变成 final 的,所以无法管理状态。
那么我们该如何解决这个问题呢?这里就需要用到有状态组件了。
在我之前的介绍有状态的组件,是不是继承了 StatefulWidget 就是成为了有状态的组件了。
好,那么我们就编写一个类,创建 MyStateful 类,继承 StatefulWidget:
class MyStateful extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
throw UnimplementedError();
}
}
发现要求实现 createState 方法,返回一个 State 对象,先不看返回什么,先来看,我们能不能像无状态组件那样定义一个变量就可以直接在组件中使用了就成为了一个有状态的组件了。
我的答案是不行的:在有状态组件中,组件被创建之后也会将组件中的变量变成 final 的
, 这里就需要用到 State 了。
State 是一个抽象类,它定义了一个 State 的子类应该实现的接口。那么我就编写一个属于 MyStateful 的 State 类,继承 State,然后再将之前的 Checkbox 组件放到 MyStatefulState 中。
class MyStatefulState extends State {
var isCheck = true;
@override
Widget build(BuildContext context) {
return Center(
child: Row(
children: [
Checkbox(
value: isCheck,
onChanged: (val) {
print(val);
this.isCheck = val asbool;
print(this.isCheck);
})
],
),
);
}
}
在 MyStateful 类 createState 方法中返回 MyStatefulState 对象:
class MyStateful extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return MyStatefulState();
}
}
运行结果:
从效果图中可以看到 isCheck 的值已经改变了,但是呢还有一个问题就是我们的数据改变了页面 UI 没有改变,这是为什么呢?
这里就需要用到 setState 方法了。因为 Flutter 和 React 一样,都是通过数据驱动 UI 的,所以当我们的数据改变了,我们需要通过 setState 方法来通知 Flutter 重新构建 UI。
setState 方法是 State 类中的一个方法,它接收一个回调函数,这个回调函数会在 setState 方法调用之后立即执行,所以我们可以在这个回调函数中改变状态。
this.setState(() {
this.isCheck = val as bool;
});