Flutter基础

Posted 怀化纱厂球迷

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter基础相关的知识,希望对你有一定的参考价值。

一.创建新的Flutter工程

  1. 使用终端进行创建 不支持大写字母,可以加下划线
    创建成功后,可以使用VSCode或者android studio打开
flutter create 项目名

  1. 工程内容
    lib文件夹:存放编写的代码,main.dart即为启动入口

  1. 项目启动
    打开ios或者安卓模拟器, 在工具栏找到启动调试,点击进行启动

二、flutter项目编写

  1. flutter项目的几个特性
    热重载 hot reload 、   热重启 restart
    运行一个flutter项目有三种方式:  冷启动:(从0启动)、 热重载 (最主要是执行build方法,其他方法不受影响)、 热重启(重新运行整个项目)
    使用热重载可以快速预览布局变化,但是只有build方法会重置

  1. 简单编写启动内容
    导入头文件(package:flutter/material.dart), 然后调用runApp函数
import 'package:flutter/material.dart';

main(List<String> args) 
  runApp(Text("Hello world", textDirection: TextDirection.ltr));

  1. 具体内容设置
    textDirection  传入文字方向
    style  设置样式,字体颜色等
    Center 也是一个Widget,实现居中操作
runApp(Center(
  child: Text("Hello world",
      textDirection: TextDirection.ltr,
      style: TextStyle(fontSize: 30, color: Colors.red))));

三、Widget介绍

  1. Widget  即组件
    万物皆是Widget, 通过嵌套Widget来实现布局功能
    • 有状态的Widget:
      StatefulWedget 在运行过程中有一些状态(data数据)需要改变
    • 无状态的Widget:
      StatelessWidget 内容是确定的,没有状态(data数据)的改变

  2. 几种常用Widget

  • MaterialApp  会自动添加某些默认配置,不需要手动进行设置,比如文字方向等
 runApp(MaterialApp(
  home: Center(
      child: Text("Hello World",
          style: TextStyle(fontSize: 30, color: Colors.orange)))));
  • Scaffold  脚手架   用于快速搭建页面  创建导航栏、Tabbar等
    主要参数有 appBar(导航栏或者Tabbar),body(界面内容)
runApp(MaterialApp(
  debugShowCheckedModeBanner: false,
  home: Scaffold(
      appBar: AppBar(
          title: Text(
        "第一个项目",
        style: TextStyle(fontSize: 20, color: Colors.white),
      )),
      body: Center(
          child: Text("Hello world",
              textDirection: TextDirection.ltr,
              style: TextStyle(fontSize: 30, color: Colors.red))))));

3.自定义Widget子类

class MyApp extends StatelessWidget 
    @override
    Widget build(BuildContext context) 
      // TODO: implement build
      return Text("hello World");
    
  

build方法: 在创建自定义Widget子类时,会执行它的build方法,返回一个希望渲染的Widget元素,例如上例所示的Text Widget

StatelessWidget没办法去主动执行build方法,当我们的数据发生改变的时候,build方法会被重新执行(setState)

  • build方法在什么情况下会执行?
    1)当对应的StatelessWidget第一次被插入到Widget树中时,即第一次创建时
    2) 当对应的父Widget发生改变时,子Widget会被重新构建
    3) 如果对应的Widget依赖inheritedWidget的一些数据,inheritedWidget数据发生改变时

将需要构造的Widget放到build方法中返回,然后直接调用编写的Widget子类,即可得到想要的界面```

main(List<String> args) 
        runApp(MyApp());
      

class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: Scaffold(
            appBar: AppBar(
                title: Text(
              "第一个项目",
              style: TextStyle(fontSize: 20, color: Colors.white),
            )),
            body: Center(
                child: Text("Hello world",
                    textDirection: TextDirection.ltr,
                    style: TextStyle(fontSize: 30, color: Colors.red)))));
  

4.代码拆分抽取
将各个Widget包装成一个个自定义Widget子类,再进行组合,完成代码的拆分重组

main(List<String> args) 
  runApp(MyApp());


class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(debugShowCheckedModeBanner: false, home: HomePageView());
  


class HomePageView extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Scaffold(
        appBar: AppBar(
            title: Text(
          "第一个项目",
          style: TextStyle(fontSize: 20, color: Colors.white),
        )),
        body: ContentBody());
  


class ContentBody extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Center(
        child: Text("Hello world",
            textDirection: TextDirection.ltr,
            style: TextStyle(fontSize: 30, color: Colors.orange)));
  

5.一行写入多个控件
横向写入多个控件  Row
竖向写入多个控件  Column

class ContentBody extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Center(
        child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
      Text("111"),
      Text("Hello world",
          textDirection: TextDirection.ltr,
          style: TextStyle(fontSize: 30, color: Colors.orange))
    ]));
  

默认从每行的最左端开始布局,使用mainAxisAlignment参数可以改变行内的布局位置


6. @immutable修饰符

  • 使用@immutable 注解标明的类及其子类均为不可变类,其中的属性均不可变,使用final修饰,不可定义状态
    Widget类使用@immutable标记,所有的Widget都不可以直接定义状态
  • StatelessWidget即是不可更改的类,不可定义状态
  • StatefulWidget内部有一个抽象方法createState,此方法返回一个State类
  • 在自定义的State子类中通过重写build方法实现控件
  • StatefulWidget内部不可直接定义状态,但是可以在自定义的State子类中可以定义状态

class ContentBody extends StatefulWidget 
  @override
  State<StatefulWidget> createState() 
    return ContentBodyState();
  


class _ContentBodyState extends State<ContentBody> 
  var flag = true;

  @override
  Widget build(BuildContext context) 
    // TODO: implement build
    return Center(
        child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
      Checkbox(
          value: flag,
          onChanged: (value) 
            setState(() 
              flag = value ?? false;
            );
          ),
      Text("Hello world",
          textDirection: TextDirection.ltr,
          style: TextStyle(fontSize: 30, color: Colors.orange))
    ]));
  

总结:
继承自StatefulWidget的类负责实现createState方法,返回一个State类
State类负责实现Build方法,以及状态的创建,来实现功能

另外需要注意的几点:

  • 状态的改变需要在setState回调执行完毕之前进行,否则不生效
  • State类一般使用下划线开头,如_ContentBodyState,下划线开头的类在别的文件无法访问,保证安全,而继承自StatefulWidget的类不加下划线

问题1:上述示例中,ContentBody返回的State类一定要定义为ContentBody泛型吗?可以改成别的StatefulWidget类吗?

可以,但没必要。
语法上无问题,但是,定义成此泛型,才可以使用系统提供的一个widget属性来构建出桥梁,连通对应的StatefulWidget子类,所以一般会这么写

问题2:继承自StatefulWidget的类,具体的作用?

1). 生成返回State类
2). 可以接收父Widget传过来的数据

四、使用StatelessWidget完成竖向列表布局

StatelessWidget子类代码块生成快捷键:  stle
上篇已经简单介绍了StatelessWidget的基本使用,接下来简单介绍如何使用StatelessWidget创建一个商品列表,效果如下:

  1. StatelessWidget参数传递
    与Dart语法一致,创建属性(final修饰),实现初始化方法,进行赋值 调用:
 
class YWHomeProduct extends StatelessWidget 
  final String title;
  final String desc;
  final String imageUrl;

  YWHomeProduct(this.title, this.desc, this.imageUrl);

  @override
  Widget build(BuildContext context) 
    return Column(children: [Text(title), Text(desc), Image.network(imageUrl)]);
  

YWHomeProduct("瓜子", "焦糖瓜子,香甜可口",
      "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.yzcdn.cn%2Fupload_files%2F2018%2F08%2F21%2FFiLdNHrxKMohbPJ2QbtOTo9MhIMm.jpg%3FimageView2%2F2%2Fw%2F580%2Fh%2F580%2Fq%2F75%2Fformat%2Fjpg&refer=http%3A%2F%2Fimg.yzcdn.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1632121924&t=b37df3a44a89fc3b768c0f3f69f94f2a")
 

2.竖向列表

  • column: 竖向布局多个控件,使用column布局,当竖向控件高度超出屏幕高度后,会显示条纹遮挡
  • ListView: 竖向可滚动控件

3.对象创建:
函数内部可以创建局部变量,减小语句内部代码量,但是每次调用函数都会创建
在类的内部创建变量,则只会在对象实例化时创建一次
在类的外部创建变量,则在整个项目生命周期中都会存在

4.创建控件之间的边距
可以通过SizedBox控件来实现,横向距离width,竖向距离height

SizedBox(height: 8)

5.边框创建
将某个控件放置到Container中,可以用Container来设置边框(decoration)以及内边距(padding)等
问题:BoxDecoration的主要用处?

return Container(
    padding: EdgeInsets.all(8),
    decoration:
        BoxDecoration(border: Border.all(color: Colors.purple, width: 8)),
    child: Column(children: [
      Text(title, style: titleStyle),
      SizedBox(height: 8),
      Text(desc, style: descStyle),
      SizedBox(height: 8),
      Image.network(imageUrl)
    ]));

用于Widget的装饰

color	      颜色背景	       Color类型
image	      图片背景	       DecorationImage类型
border	      边界	        BoxBorder类型
borderRadius  圆角边界半径	 BorderRadiusGeometry类型
boxShadow	  阴影  	        List<BoxShadow>类型
gradient	  渐变色	       Gradient类型
backgroundBlendMode	背景混合模式	  BlendMode类型
shape	      形状	        BoxShape类型

6.控制column竖向布局控件位置
控件分为主轴(竖向)和交叉轴(横向)
主轴使用MainAxisAlignment控制子控件位置
交叉轴使用CrossAxisAlignment控制子控件位置
通过Flex可以决定主轴和交叉轴

Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
      Text(title, style: titleStyle),
      SizedBox(height: 8),
      Text(desc, style: descStyle),
      SizedBox(height: 8),
      Image.network(imageUrl)
    ])

下面是完整的代码:

import 'package:flutter/material.dart';

main(List<String> args) 
  runApp(MyApp());


class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(debugShowCheckedModeBanner: false, home: YWHomePage());
  


class YWHomePage extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Scaffold(appBar: AppBar(title: Text("购物车")), body: YWBodyContent());
  


class YWBodyContent extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return ListView(children: [
      YWHomeProduct("瓜子", "焦糖瓜子,香甜可口",
          "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.yzcdn.cn%2Fupload_files%2F2018%2F08%2F21%2FFiLdNHrxKMohbPJ2QbtOTo9MhIMm.jpg%3FimageView2%2F2%2Fw%2F580%2Fh%2F580%2Fq%2F75%2Fformat%2Fjpg&refer=http%3A%2F%2Fimg.yzcdn.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1632121924&t=b37df3a44a89fc3b768c0f3f69f94f2a"),
      YWHomeProduct("花生", "盐水花生,补气养血",
          "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi2.chuimg.com%2F25d9caf08b6811e6a9a10242ac110002_1171w_801h.jpg%3FimageView2%2F2%2Fw%2F660%2Finterlace%2F1%2Fq%2F90&refer=http%3A%2F%2Fi2.chuimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1632122384&t=f3859622328496babe08d62ffca276ba"),
      YWHomeProduct("八宝粥", "居家旅行,出门良品",
          "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fcp2.douguo.net%2Fupload%2Fdish%2F7%2F3%2F3%2F600_7355c671e3bf52100c61e043c6110ae3.jpg&refer=http%3A%2F%2Fcp2.douguo.net&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1632122038&t=cedcc17ba9ed999844c9c3c1bedf2ded")
    ]);
  


class YWHomeProduct extends StatelessWidget 
  final String title;
  final String desc;
  final String imageUrl;
  final titleStyle = TextStyle(fontSize: 20, color: Colors.orange);
  final descStyle = TextStyle(fontSize: 15, color: Colors.blue);

  YWHomeProduct(this.title, this.desc, this.imageUrl);

  @override
  Widget build(BuildContext context) 
    return Container(
        padding: EdgeInsets.all(8),
        decoration:
            BoxDecoration(border: Border.all(color: Colors.purple, width: 8)),
        child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
          Text(title, style: titleStyle),
          SizedBox(height: 8),
          Text(desc, style: descStyle),
          SizedBox(height: 8),
          Image.network(imageUrl)
        ]));
  

五、StatefulWidget的使用

  1. StatefulWidget的使用
    有状态需要改变的时候要使用StatefulWidget
  • StatefulWidget内部有一个抽象方法createState,此方法返回一个State类,继承自StatefulWidget的类需要实现此方法
  • 在自定义的State子类中需要实现build方法生成一个控件
  • StatefulWidget内部不可直接定义状态,但是可以在自定义的State子类中可以定义状态

问题:为什么Flutter在设计的时候,StatefulWidget的build方法放在State中?

  • build出来的widget是需要依赖State中的变量(状态/数据)
  • 在Flutter的运行过程中,widget是不断销毁和创建的,当我们的状态发生改变时,并不希望重新创建一个新的State

  1. 使用StatefulWidget构建一个计数器
    效果图如下:


    其中按钮可以使用ElevatedButton(RaisedButton已废弃)
    使用Column构建竖向组件,Row构建横向组件

import 'package:flutter/material.dart';

main(List<String> args) 
  runApp(MyApp());


class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(debugShowCheckedModeBanner: false, home: YWHomePage());
  


class YWHomePage extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Scaffold(appBar: AppBar(title: Text("购物车")), body: YWHomeContent());
  


class YWHomeContent extends StatefulWidget 
  @override
  _YWHomeContentState createState() => _YWHomeContentState();


class _YWHomeContentState extends State<YWHomeContent> 
  int number = 0;
  @override
  Widget build(BuildContext context) 
    return Center(
        child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
      getButtons(),
      Text("当前计数:$number", style: TextStyle(fontSize: 20))
    ]));
  

  Widget getButtons() 
    return Row(mainAxisAlignment: MainAxisAlignment.center, children: [
      ElevatedButton(
          child: Text("+", style: TextStyle(fontSize: 20)),
          style: ButtonStyle(
              backgroundColor: MaterialStateProperty.all(Colors.pink)),
          onPressed: () 
            setState(() 
              number++;
            );
          ),
      ElevatedButton(
          child: Text("-", style: TextStyle(fontSize: 20)),
          style: ButtonStyle(
              backgroundColor: MaterialStateProperty.all(Colors.purple)),
          onPressed: () 
            setState(() 
              if (number > 0) 
                number--;
              
            );
          )
    ]);
  

问题:数值改变能放在setState外面吗?

setState会触发widget build方法重新进行渲染

  1. StatefulWidget传值
    父类给子类传值,传值方式与StatelessWdight语法一致,创建属性(final修饰),实现对应初始化方法,进行赋值,将参数传递给Widget子类
    然后在State类中通过this.widget来实现取值
class YWHomeContent extends StatefulWidget 
  final String message;
  YWHomeContent(this.message);
  @override
  _YWHomeContentState createState() =>_YWHomeContentState();


class _YWHomeContentState extends State<YWHomeContent> 
  int number = 0;
  @override
  Widget build(BuildContext context) 
    return Text("$this.widget.message");
  

这也就是为什么State类定义的泛型需要跟之前创建的StatefulWidget子类保持一致的原因

六、常用Widget

查看Widget组件目录

  1. Text  文本
    1.1  普通文本展示
    • 文本布局参数:如对齐方式TextAlign、排版方向TextDirection、最大行数maxLines、文本超出限制规则overflow、textScaleFactor: 文本缩放因子等,这些都是构造函数参数
    • 控制文本样式的参数:如字体名称fontFamily、 字体大小fontSize、颜色color、文本阴影shadow等,这些都被封装到了构造函数的参数style中
return Center(
    child: Text("调用YWHomeContent的初始化方法",
        textAlign: TextAlign.center,
        maxLines: 1,
        overflow: TextOverflow.ellipsis,
        textScaleFactor: 0.8,
        style: TextStyle(
            fontSize: 30,
            color: Colors.red,
            fontWeight: FontWeight.normal)));

1.2 富文本
富文本使用Text.rich进行创建,必传参数textSpan,可以使用TextSpan进行构造,其内部可以传一个widget,也可以传一个Children数组

return Center(
    child: Text.rich(TextSpan(children: [
  TextSpan(text: "Hello world", style: TextStyle(fontSize: 30)),
  WidgetSpan(child: Icon(Icons.favorite, color: Colors.red)),
  TextSpan(text: "Hello flutter", style: TextStyle(fontSize: 20))
])));

  1. Button 按钮
    2.1 ElevatedButton   (RaisedButton  凸起按钮  ?)
    2.2 TextButton  (FlatButton 平坦按钮  ?)
    2.3 OutlinedButton  边框按钮
    2.4 FloatingActionButton  悬浮按钮
    常用于脚手架Scaffold的入参
    2.5 自定义button  图标文字背景圆角等等

 
TextButton(
      onPressed: () 
        print("TextButton clicked");
      ,
      child: Text("TextButton"),
      style: ButtonStyle(
          foregroundColor: MaterialStateProperty.all(Colors.white),
          backgroundColor: MaterialStateProperty.all(Colors.orange)))
OutlinedButton(
      onPressed: () 
        print("OutlinedButton clicked");
      ,
      child: Text("OutlinedButton"),
      style: ButtonStyle(
          foregroundColor: MaterialStateProperty.all(Colors.white),
          backgroundColor: MaterialStateProperty.all(Colors.blue)))
FloatingActionButton(
      onPressed: () 
        print("FloatingActionButton clicked");
      ,
      child: Icon(Icons.add))
TextButton(
      onPressed: () 
        print("自定义button点击");
      ,
      child: Row(
          children: [Icon(Icons.add), Text("添加好友")],
          mainAxisSize: MainAxisSize.min),
      style: ButtonStyle(
          shape: MaterialStateProperty.all(RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(8))),
          foregroundColor: MaterialStateProperty.all(Colors.white),
          backgroundColor: MaterialStateProperty.all(Colors.orange)))
 

  1. Image 图片
    3.1 从网络获取图片 NetworkImage
    通过url链接加载对应图片,
    fit属性控制图片裁切模式,为BoxFit类型,其中fitWidth为宽度固定,自动适配高度;fitHeight为高度固定,自动适配宽度
    repeat属性控制空白部分是否进行图片重复填充,跟fit模式相关
    alignment控制图片裁切完后在视图内展示的位置,可以直接使用宏定义,也可以自定义,Alignment(0, 0)表示居中,值为-1 —— 1
    color和colorBlendMode负责控制颜色混入配置
return Image(
  image: NetworkImage(imageUrl),
  width: 300,
  height: 300,
  fit: BoxFit.contain,
  repeat: ImageRepeat.repeatY,
  alignment: Alignment(1, 1),
  color: Colors.pink,
  colorBlendMode: BlendMode.color,
);

简写模式如下:

Image.network(imageUrl)

问题:(-1,-1)代表什么位置?
问题: 如何设置图片占位图?


3.2 从本地获取图片
步骤如下:

    • 在flutter项目中创建一个文件夹,用于存储图片
      1x图片放到指定文件夹内,如果有2x、3x图片,在1x图片所在文件夹内建立2.0x文件夹、3.0x文件夹放置
    • 在pubspec.yaml进行配置
      找到assets:配置处,将注释取消掉
      添加对应图片路径配置 配置完成后执行下flutter packages get重新获取依赖
assets:
- assets/Images/
    • 使用图片
return Image(image: AssetImage("assets/Images/001.jpg"));

3.3 实现圆角头像

    • 使用CircleAvatar组件
return CircleAvatar(
    backgroundImage: AssetImage("assets/Images/001.jpg"),
    radius: 50,
    child: Container(
    alignment: Alignment(0, 0),
    width: 100,
    height: 20,
    child: Text("兵长利威尔")));

其中backgroundImage传入抽象类ImageProvider图片,radius为半径,child为内部widget

    • 使用ClipOval组件
ClipOval(
      child: Image(
    image: NetworkImage(imageUrl),
    width: 100,
    height: 100,
    fit: BoxFit.cover,
  ))

3.4 实现圆角图片

    • ClipRRect
ClipRRect(
      borderRadius: BorderRadius.circular(80),
      child: Image.network(imageUrl,
          width: 160, height: 160, fit: BoxFit.cover))

七、StatelessWidget、StatefulWidget生命周期

  1. 生命周期的作用(意义)
  • 初始化一些数据、状态、变量
  • 发送网络请求时机
  • 进行一些事件的监听
  • 管理内存(手动销毁)

  1. StatelessWidget生命周期
    这里比较简单,只有两个生命周期
  • 构造函数被调用
  • 调用build方法

class TestWidget extends StatelessWidget 
  TestWidget() 
    print("构造函数被创建");
  

  @override
  Widget build(BuildContext context) 
    print("build方法被调用");
    return Container();
  

  1. StatefulWidget生命周期

  • 第一步:调用StatefulWidget子类的构造方法
  • 第二步:调用StatefulWidget子类的createState方法
  • 第三步:调用State子类的构造方法
  • 第四步 调用State子类的initState方法
    用于状态的初始化,会在State类完成构造之后,build方法执行之前,进行执行
    initState方法使用@mustCallSuper标记,必须调用父类initState方法
  • 第五步:执行State子类的build方法
  • 最后,当前的Widget子类不再使用时,会执行State子类的dispose方法,进行内存的释放

此外:

  • didChangeDependencies方法  在initState方法执行后,或者从其他对象中依赖一些数据发生改变时,比如inheritedWidget,会执行此方法
  • didUpdateWidget方法  当重新创建Widget子类(rebuild),会触发state子类的didUpdateWidget方法

代码如下所示:

class YWHomeContent extends StatefulWidget 
  YWHomeContent() 
    print("1.调用YWHomeContent的初始化方法");
  
  @override
  _YWHomeContentState createState() 
    print("2.调用YWHomeContent的createState方法");
    return _YWHomeContentState();
  


class _YWHomeContentState extends State<YWHomeContent> 
  _YWHomeContentState() 
    print("3.调用_YWHomeContentState的初始化方法");
  

  @override
  void initState() 
    super.initState();
    print("4.调用_YWHomeContentState的initState方法");
  

  @override
  void didChangeDependencies() 
    print("调用_YWHomeContentState的didChangeDependencies方法");
    super.didChangeDependencies();
  

  @override
  void didUpdateWidget(covariant YWHomeContent oldWidget) 
    print("调用_YWHomeContentState的didUpdateWidget方法");
    super.didUpdateWidget(oldWidget);
  

  @override
  Widget build(BuildContext context) 
    print("5.调用_YWHomeContentState的build方法");
    return Text("Hello World");
  

  @override
  void dispose() 
    super.dispose();
    print("6.调用_YWHomeContentState的dispose方法");
  

  • setState方法会在内部触发重新运行build方法,根据最新的状态返回新的控件,所以状态的修改需要在setState方法中进行

  1. StatefulWidget生命周期的复杂版
    在StatefulWidget普通生命周期的基础上,还有一些复杂的过程未提及到

  • mounted
  • dirty state & clean state
    疑问:意义及用法?

以上是关于Flutter基础的主要内容,如果未能解决你的问题,请参考以下文章

(40万-70万)瓜子二手车网找后端数据挖掘工程师

为安卓开发者介绍的移动开发框架 Flutter

Flutter基础篇——常用Widget

《Flutter 动画系列二》Google工程师带你选择Flutter动画控件

瓜子二手车的谎言!

瓜子二手车爬虫源码