《Flutter实战》第三章

2020-04-29  本文已影响0人  番茄tomato
  • 本篇参考资料《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中的保存的状态信息可以:

State中有两个常用属性:

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()之后。
在调用didUpdateWidget()之后。
在调用setState()之后。
在调用didChangeDependencies()之后。
在State对象从树中一个位置移除后(会调用deactivate)又重新插入到树的其它位置之后。

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就会错误提示
上一篇下一篇

猜你喜欢

热点阅读