Flutter 即学即用系列博客——05 StatelessWidget vs S

来源:http://www.chinese-glasses.com 作者:Web前端 人气:143 发布时间:2020-04-22
摘要:时间: 2019-09-20阅读: 359标签: 状态 当我们使用编译器创建一个新Flutter应用的时候,我们可以在主界面看到两个小部件StatelessWidget和StatefulWidget。这是两个最常见使用最频繁的小部件了。

时间: 2019-09-20阅读: 359标签: 状态

图片 1

当我们使用编译器创建一个新Flutter应用的时候,我们可以在主界面看到两个小部件StatelessWidget和StatefulWidget。这是两个最常见使用最频繁的小部件了。

上一篇我们对 Flutter UI 有了一个基本的了解。

StatelessWidget ,StatefulWidgetStatelessWidget 状态不可改变的,它内部的数值和UI都应该是常量不可改变StatefulWidget 状态可变,我们可以通过点击,或者网络获取数据等来动态的改变界面。

这一篇我们通过自定义 Widget 来了解下如何写一个 Widget?

假如我们要实现如下图的功能,点击加一。如果使用StatelessWidget,会发现点击的时候count是加一了,但是界面没有刷新。应该使用StatefulWidget,当count加一的时候通过setState(() { _count++;});方法来改变count的值,这时候就发现界面可以刷新了。

然而 Widget 有两个,StatelessWidget 和 StatefulWidget,我们要继承哪一个?

比如

下面让我们跟着文章来探索一番。

import 'package:flutter/material.dart';class StateManagerDemo extends StatefulWidget { @override _StateManagerDemoState createState() = _StateManagerDemoState();}class _StateManagerDemoState extends StateStateManagerDemo { int _count = 0; void countCallBack(){ setState(() { _count++; }); debugPrint('$_count'); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('StateManagerDemo'), elevation: 0.0, ), body: Counter0(_count), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: countCallBack, ), ); }}//------class Counter0 extends StatelessWidget { final int count; Counter0(this.count); @override Widget build(BuildContext context) { return Center( child: Chip( label: Text('$count'), ), ); }}//-----class Counter extends StatelessWidget { final int count; final VoidCallback voidCallback; Counter(this.count,this.voidCallback); @override Widget build(BuildContext context) { return Center( child: ActionChip( label: Text('$count'), onPressed: voidCallback, ), ); }}

图片 2

上面的代码中,StateManagerDemo有两个子部件Counter0和Counter。当我们点击按钮的时候,_count的值++,然后传递给子部件。

我们先来看下继承的 Widget 为 StatelessWidget 的情况。

Counter0是直接接受父部件传过来的参数。

第一步:新建一个文件 bold_text.dart

Counter不仅接收父部件传过来的参数,还有一个回调。这样点击它的时候,会执行父部件中的回调方法,也能改变自身的显示。

图片 3图片 4这里文件名后面后缀 .dart 可带可不带

InheritedWidget

文件名多个单词组成用下划线分隔。

上面的情况是只有一层,比如Counter小部件中使用了父部件的count这个变量,假如Counter没有用到这个变量而是它的子类用到了这个变量,我们还要一层一层的传下去吗,这有点麻烦啊,这时候可以使用InheritedWidget这个类来管理。

这里我们演示直接在 lib 文件夹下面创建,实际项目记得文件夹结构的组织哦~

import 'package:flutter/material.dart';//使用InheritedWidget来管理状态,class ContentProvider extends InheritedWidget { final int count; final VoidCallback countCallBack; final Widget child; const ContentProvider({ this. count, this. countCallBack, this. child, }): assert(child != null), super(child: child); static ContentProvider of(BuildContext context) { return context.inheritFromWidgetOfExactType( ContentProvider) as ContentProvider; } //是否通知继承该小部件的小部件更新 @override bool updateShouldNotify(ContentProvider old) { return true; }}class StateManagerDemo extends StatefulWidget { @override _StateManagerDemoState createState() = _StateManagerDemoState();}class _StateManagerDemoState extends StateStateManagerDemo { int _count = 0; void countCallBack(){ setState(() { _count++; }); debugPrint('$_count'); } @override Widget build(BuildContext context) { //ContentProvider放在最外层,指定参数count和callback return ContentProvider( count: _count, countCallBack: countCallBack, child: Scaffold( appBar: AppBar( title: Text('StateManagerDemo'), elevation: 0.0, ), body: Counter1(), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: countCallBack, ), ), ); }}class Counter1 extends StatelessWidget { @override Widget build(BuildContext context) { //直接使用ContentProvider中的参数 final int count = ContentProvider.of(context).count; final VoidCallback voidCallback = ContentProvider.of(context).countCallBack; return Center( child: ActionChip( label: Text('$count'), onPressed: voidCallback, ), ); }}

第二步:import 系统包

首先定义一个数据提供者ContentProvider继承InheritedWidget,里面定义我们需要的count和回调。提供一个of方法让外界可以拿到它的实例,方便拿到方法。

一般自定义 Widget 都要 import 下面的一个包。

然后将这个ContentProvider放在主布局的最外层,并传入需要的参数count和callBack。这样它的子部件中就都能访问到这个参数了。

import 'package:flutter/material.dart';

最后在子部件Counter1中直接使用ContentProvider中的参数。

IDE 有自动提示和补全功能,因此不用死记硬背。

ScopedModel

图片 5

还可以使用ScopedModel来完成状态管理

第三步:自定义一个类继承自 StatelessWidget

这是一个第三方的库,该库最初是从Fuchsia代码库中提取的,使用时需要先导入包,在pubspec.yaml文件中添加依赖

一般类名跟文件名一致就可以,采用驼峰格式命名。

dependencies: scoped_model: 1.0.1
import 'package:flutter/material.dart';class BoldText extends StatelessWidget { }

?q=scoped_model这里可以看到最新版本。使用这个库的时候我们在StatelessWidget中也可以改变UI

第四步:实现一个需要 override 的方法 build

该库主要分为三个部分

import 'package:flutter/material.dart';class BoldText extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return null; }}

Model class 扩展此类以创建自己的模型,例如SearchModel或UserModel。我们可以监听数据的变化!ScopedModel 小部件 把Model包装到ScopedModel中,它内部的所有部件都能拿到model中的数据ScopedModelDescendant 只要Model发生改变 它就会改变

一般第三步操作之后 IDE 有提示,直接使用快捷修复自动追加 build 代码即可。如下图:

比如用ScopedModel实现前面的功能

图片 6

import 'package:flutter/material.dart';import 'package:scoped_model/scoped_model.dart';class CountModel extends Model{ int _count = 0; int get count =_count; void countIncrease(){ _count ++; //通知改变 notifyListeners(); }}class StateModelDemo extends StatelessWidget { @override Widget build(BuildContext context) { return ScopedModelCountModel( model: CountModel() , child: Scaffold( appBar: AppBar( title: Text('StateModelDemo'), ), body: Counter2(), floatingActionButton: ScopedModelDescendantCountModel( rebuildOnChange: false, builder: (context, child, model) = FloatingActionButton( child: Icon(Icons.add), onPressed: (){ model.countIncrease(); }, ), ), ), ); }}class Counter2 extends StatelessWidget { @override Widget build(BuildContext context) { return ScopedModelDescendantCountModel( builder: (context, child, model) = Center( child: ActionChip( label: Text('${model.count}'), onPressed: (){ model.countIncrease(); }, ), ), ); }}

第五步:实现 Widget

Mobx

上述代码的 TODO 表示我们要在里面实现对应的 Widget。所以我们删除 TODO,然后在写我们要返回的 Widget 来替换 null 即可。

Mobx在前端中用的多,且很好用,所以Flutter也引入了,对于我们Android开发者来说跟学前面的成本都一样哈哈。

我们写一个单独的方法 _buildWidget 来返回 Widget,同时返回我们之前写的 Text,如下:

查看版本:?q=Mobxgithub:_codegen

import 'package:flutter/material.dart';class BoldText extends StatelessWidget { @override Widget build(BuildContext context) { return _buildWidget(); } Widget _buildWidget() { return Text( 'Hello, world!', textDirection: TextDirection.ltr, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, style: TextStyle(fontWeight: FontWeight.bold), ); }}

首先需要去pubspec.yaml文件中引入依赖

可以看到我们这个 Widget 应该会显示成上篇我们界面所见的粗体文本。

dependencies: mobx: ^0.3.8 flutter_mobx: ^0.3.3dev_dependencies: mobx_codegen: ^0.3.9 build_runner: ^1.7.0

但是这里 Hello, world! 写死了,我们要让这个自定义 Widget 通用一些,可以定义一个必传参数文本内容,修改如下:

然后开始使用mobx完成之前的功能

import 'package:flutter/material.dart';class BoldText extends StatelessWidget { final String data; BoldText(this.data); @override Widget build(BuildContext context) { return _buildWidget(); } Widget _buildWidget() { return Text( data, textDirection: TextDirection.ltr, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, style: TextStyle(fontWeight: FontWeight.bold), ); }}

首先创建一个store类

可以看到我们定义了一个变量,通过构造函数让外部传进来。

import 'package:mobx/mobx.dart';//包含生成的文件part 'state_manager_demo.g.dart';class Counter = _Counter3 with _$Counter;abstract class _Counter3 with Store { @observable int value = 0; @action void increment() { value++; }}

这里的 BoldText(this.data); 等价于 Android 下面代码:

创建完成之后,我们会发现part后面的内容和_$Counter都会报错。说是找不到,需要我们生成。来到AndroidStudio的terminal窗口执行下面命令来生成文件

 BoldText(String data) { this.data = data; }
flutter packages pub run build_runner build

可以看到 dart 的语法糖简化了写法。具体更多构造函数写法可以查看 dart 官网。

这时候就可以看到我们自己的文件state_manager_demo.dart文件旁边生成了一个新文件state_manager_demo.g.dart,而且不在报错了。

图片 7

如果想要修改后.g.dart文件也能自动修改执行

我们以之前的 main.dart 为例进行讲解。

pub run build_runner watch
import 'package:flutter/material.dart';void main() => runApp;class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: Text( 'Hello, world!', textDirection: TextDirection.ltr, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, style: TextStyle(fontWeight: FontWeight.bold), ), ); }}

下面去界面中使用它

第一步:导入我们的自定义 Widget 包

class MobxDemo extends StatefulWidget { @override _MobxDemoState createState() = _MobxDemoState();}class _MobxDemoState extends StateMobxDemo { final Counter counter = Counter(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('StateModelDemo'), ), body: Center( child: Observer( builder: (_)=ActionChip( label: Text('${counter.value}'), onPressed: (){ counter.increment(); }, ), ), ), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed:(){ counter.increment(); }, ), ); }}

相对路径:

很简单继承StatefulWidget,需要监听的小部件使用Observer包裹起来。然后创建一个成员变量final Counter counter = Counter();内部就可以直接使用Counter中的变量和方法了。运行效果跟前面的一样。

import 'bold_text.dart';

综合来看这几种状态管理的方式 ,我感觉Mobx是最好用的一个。

绝对路径:

原文 基础-状态管理/

import 'package:my_flutter/bold_text.dart';

上面任选其一即可。主要是相对路径和绝对路径的区别。

第二步:使用

import 'package:flutter/material.dart';import 'bold_text.dart';void main() => runApp;class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: BoldText('Hello, world!'), ); }}

对比可以看到节省了很多代码行,尤其对于有多个地方用到的公共组件更加可以这样处理。

FileName为你文件名的驼峰形式:

import 'package:flutter/material.dart';class FileName extends StatelessWidget { @override Widget build(BuildContext context) { return _buildWidget(); } Widget _buildWidget() { //TODO build your widget }}

本文由10bet发布于Web前端,转载请注明出处:Flutter 即学即用系列博客——05 StatelessWidget vs S

关键词:

最火资讯