《Flutter实战》第三章

  • 本篇参考资料《Flutter实战》
  • 本篇文章只是本人看书的理解和整理的笔记,更完整的内容还在书上!
  • 电子书链接:https://book.flutterchina.club/
  • Flutter中文社区链接:https://flutterchina.club/
  • 尊重原作者,能支持购买实体书当然最好
    3.1~3.2

一.Flutter项目的基本结构和文件命名方式

我们来看一个例子:github上5k星的flutter项目:在flutter上实现豆瓣APP
https://github.com/kaina404/FlutterDouBan

项目结构

这个是一个值得学习的大型Flutter项目结构,有很多代码d本人没有看懂,以后再补充,我们可以看到,这个项目结构是根据代码的不同功能区分的很清楚
同时补充一下dart文件和class命名:我们打开pages包
image.png

那么在我们的dart文件是按不用的功能放在不同的包里,那android studio怎么新建dart包呢?如下图:
image.png

这里我们新建了一个error_dart,显示错误信息界面、
image.png

在main使用需引入:为引入就使用时会报错直接alt+enter就可以看到IDE提供的解决方案

import 'package:tomatoflutterapp/pages/error_page.dart';

二 StatelessWidget和StatefulWidget

之前已经提到过,书上也讲的很清楚,复制黏贴没有意义,直接跳转
https://book.flutterchina.club/chapter3/flutter_widget_intro.html
StatelessWidget:是没有状态,不可改变的界面
StatefulWidget:是有状态的,界面内容可以根据状态进行改变

其中无状态界面StatelessWidget是直接在其内部重写build方法构建出界面,
但是有状态界面内通常是有两个类组成StatefulWidgetState,build在State中,同时State中还可以调用setState进行刷新

2.1 State

一个StatefulWidget类会对应一个State类,State表示与其对应的StatefulWidget要维护的状态,State中的保存的状态信息可以:

  • 在widget 构建时可以被同步读取。
  • 在widget生命周期中可以被改变,当State被改变时,可以手动调用其setState()方法通知Flutter framework状态发生改变,Flutter framework在收到消息后,会重新调用其build方法重新构建widget树,从而达到更新UI的目的。

State中有两个常用属性:

  • widget,它表示与该State实例关联的widget实例,由Flutter framework动态设置。注意,这种关联并非永久的,因为在应用生命周期中,UI树上的某一个节点的widget实例在重新构建时可能会变化,但State实例只会在第一次插入到树中时被创建,当在重新构建时,如果widget被修改了,Flutter framework会动态设置State.widget为新的widget实例。
    通过weiget可以获取到StatefulWidget的一些属比如weiget.title

  • context。StatefulWidget对应的BuildContext,作用同StatelessWidget的BuildContext。

State的生命周期

State生命周期

为了更好的理解我们在计数器里重写生命中周期函数

  //生命周期
  @override
  void initState() {
    super.initState();
    _counter = widget.initValue; //使用widget属性 获得与之关联的widget实例初始值
    print("initState");
  }

@override
  Widget build(BuildContext context) {
print("build");
...
}

  @override
  void didUpdateWidget(MyStatefulWidgetPage oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("didUpdateWidget");
  }

  @override
  void deactivate() {
    super.deactivate();
    print("deactive");
  }

  @override
  void dispose() {
    super.dispose();
    print("dispose");
  }
  @override
  void reassemble() {
    super.reassemble();
    print("reassemble");
  }
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("didChangeDependencies");
  }

运行程序进入计数器界面我们可以看到生命周期initState-->didChangeDependencies-->build

I/flutter ( 5042): initState
I/flutter ( 5042): didChangeDependencies
I/flutter ( 5042): build

点击“+”我们可以看到:build被重复调用 界面重构了

D/SettingsInterface( 5042):  from settings cache , name = sound_effects_enabled , value = 0
I/flutter ( 5042): build
D/SettingsInterface( 5042):  from settings cache , name = sound_effects_enabled , value = 0
I/flutter ( 5042): build
D/SettingsInterface( 5042):  from settings cache , name = sound_effects_enabled , value = 0
I/flutter ( 5042): build

我们点击热加载时:

Performing hot reload...                                               
|I/flutter ( 5042): reassemble
/I/flutter ( 5042): didUpdateWidget
I/flutter ( 5042): build

当我们退出界面:

I/flutter ( 5042): deactive
I/flutter ( 5042): dispose

下边是书上对各个生命周期的解释:

  • initState:当Widget第一次插入到Widget树时会被调用,对于每一个State对象,Flutter framework只会调用一次该回调,所以,通常在该回调中做一些一次性的操作,如状态初始化、订阅子树的事件通知等。不能在该回调中调用BuildContext.dependOnInheritedWidgetOfExactType(该方法用于在Widget树上获取离当前widget最近的一个父级InheritFromWidget,关于InheritedWidget我们将在后面章节介绍),原因是在初始化完成后,Widget树中的InheritFromWidget也可能会发生变化,所以正确的做法应该在在build()方法或didChangeDependencies()中调用它。
  • didChangeDependencies():当State对象的依赖发生变化时会被调用;例如:在之前build() 中包含了一个InheritedWidget,然后在之后的build() 中InheritedWidget发生了变化,那么此时InheritedWidget的子widget的didChangeDependencies()回调都会被调用。典型的场景是当系统语言Locale或应用主题改变时,Flutter framework会通知widget调用此回调。
  • build():此回调读者现在应该已经相当熟悉了,它主要是用于构建Widget子树的,会在如下场景被调用:

在调用initState()之后。
在调用didUpdateWidget()之后。
在调用setState()之后。
在调用didChangeDependencies()之后。
在State对象从树中一个位置移除后(会调用deactivate)又重新插入到树的其它位置之后。

  • reassemble():此回调是专门为了开发调试而提供的,在热重载(hot reload)时会被调用,此回调在Release模式下永远不会被调用。
  • didUpdateWidget():在widget重新构建时,Flutter framework会调用Widget.canUpdate来检测Widget树中同一位置的新旧节点,然后决定是否需要更新,如果Widget.canUpdate返回true则会调用此回调。正如之前所述,Widget.canUpdate会在新旧widget的key和runtimeType同时相等时会返回true,也就是说在在新旧widget的key和runtimeType同时相等时didUpdateWidget()就会被调用。
  • deactivate():当State对象从树中被移除时,会调用此回调。在一些场景下,Flutter framework会将State对象重新插到树中,如包含此State对象的子树在树的一个位置移动到另一个位置时(可以通过GlobalKey来实现)。如果移除后没有重新插入到树中则紧接着会调用dispose()方法。
  • dispose():当State对象从树中被永久移除时调用;通常在此回调中释放资源。

3.2 暂时跳过

3.3~3.8是关于每个单独控件的使用,书上是说明很清楚,例子也很丰富,建议先全部浏览完毕后,对于某个控件想深入了解再去搜索

补充点:

3.6:单选框和复选框

注意每个单选框/复选框 都需要有一个value 布尔变量对应,首先给初始值,然后点击的时候需要
onChanged中调用setState方法来刷新界面才能看到点击效果

class _MySwitchPageState extends State<MySwitchPage> {
  bool _switchSelected = true; //维护单选开关状态
.....
            Switch(
              value: _switchSelected, //给予一个初始值
              onChanged: (value) {
                //在点击后会调用onChanged 传入改变后的值
                //重新构建页面
                setState(() {
                  //修改状态值
                  _switchSelected = value;
                });
              },
            )

.....}

单独使用Switch和Checkbox就只是一个开关


image.png

那我们想要有一些文字说明怎么做呢?


image.png

这时候要用到Switch和Checkbox的上层封装类 SwitchListTile / CheckboxListTile

            SwitchListTile(
              secondary: Icon(
                Icons.block,
                color: Colors.pink[200],
              ),
              title: Text("关闭通知"),

              value: _switchSelected, //给予一个初始值
              onChanged: (value) {
                //在点击后会调用onChanged 传入改变后的值
                //重新构建页面
                setState(() {
                  //修改状态值
                  _switchSelected = value;
                });
              },
            ),
            CheckboxListTile(
              title: Text("硬件加速"),
              secondary: Icon(
                Icons.access_alarm,
                color: Colors.pink[200],
              ),
              value: _checkboxSelected,
              onChanged: (value) {
                setState(() {
                  //修改状态值
                  _checkboxSelected = value;
                });

              },
            )

3.7:输入框和表单

书上的不是很好理解
参考链接:https://www.jianshu.com/p/54419a143d70

3.7.1 controller的一般用法:
class _MyInputPageState extends State<MyInputPage> {

  //定义一个Controller
  TextEditingController _myDemoController = TextEditingController();

  void initDemoController() {
    //设置默认值
    _myDemoController.text = "hello world!";
    //第三个字符开始选中后面的字符
    _myDemoController.selection = TextSelection(
        baseOffset: 2, extentOffset: _myDemoController.text.length);
    //监听输入改变
    _myDemoController.addListener(() {
      //这里是只要输入框的内容变了都可以监听到
      print(_myDemoController.text);
    });
  }
  @override
  void initState() {
    super.initState();
    initDemoController();
  }
.....
}

在TextField中设置controller

            TextField(
              //设置controller,可以通过controller完成很多很多工作
              controller: _myDemoController,
              textInputAction: TextInputAction.search,
            )
3.7.2 onEditingComplete和onSubmitted

onEditingComplete和onSubmitted:这两个回调都是在输入框输入完成时触发,比如按了键盘的完成键(对号图标)或搜索键(🔍图标)。不同的是两个回调签名不同,onSubmitted回调是ValueChanged<String>类型,它接收当前输入内容做为参数,而onEditingComplete不接收参数。

            TextField(
              //设置controller,可以通过controller完成很多很多工作
              controller: _myDemoController,
              textInputAction: TextInputAction.search,
              //输入完成时调用,比如点击了搜索,但是无参数
              //可以使用controller获取到输入内容
              onEditingComplete: (){
                print("onEditingComplete: ${_myDemoController.text}");
              },

              //输入完成时调用,这里有参数是输入的内容input
              onSubmitted: (input){
                print("onSubmitted:$input");
              },
            )
image.png

我突然发现这种decoration设计属性可以简单封装一下,每次调用就会节省很多时间,风格也统一了

3.7.3 用很笨的一个方式来实现一个错误输入提醒

效果(主要学习整理一个思想,慢慢改进,记录一个代码优化的思路)


错误提示

我们知道error信息是在InputDecoration中显示的,其实如果在InputDecoration构造函数中设置了errorText,并传入null,那么和不设置其实是一个效果,所以我们设置一个errorMsg变量,让他随输入的内容变化:

  //错误信息
  String errorMsg;
  void initDemoController() {
    //设置默认值
    _myDemoController.text = "hello world!";
    //监听输入改变
    _myDemoController.addListener(() {
      //这里是只要输入框的内容变了都可以监听到
      print(_myDemoController.text);
      setState(() {
        //每次输入字符则改变界面状态
        if (_myDemoController.text.length > 18) {
          //如果长度大于18就显示
          errorMsg = "输入内容太长";
        } else {
          errorMsg = null;
        }
      });
    });
  }

在TextField中设置

        TextField(
              //设置controller,可以通过controller完成很多很多工作
              controller: _myDemoController,
              textInputAction: TextInputAction.search,
              decoration: InputDecoration(
                  labelText: "搜索",
                  hintText: "输入搜索内容",
                  prefixIcon: Icon(Icons.search),
                  fillColor: Colors.blue[200],
                  filled: true,
                  errorText: errorMsg),
            )

但是我们发现这个错误提示显示了还是可以继续输入,不许用户输入怎么操作?

              maxLength: 18,//设置最长输入
              maxLengthEnforced: true,//true的话就阻止超过,false就会错误提示
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351