Flutter生命周期、基础Widget介绍

Flutter生命周期、基础Widget介绍,第1张

Flutter生命周期、基础Widget介绍 一.创建新的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 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 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 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 createState() {
    return ContentBodyState();
  }
}

class _ContentBodyState extends State {
  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://www.kaotop.com/skin/sinaskin/image/nopic.gif")
 

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类型
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 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://www.kaotop.com/skin/sinaskin/image/nopic.gif"),
      YWHomeProduct("花生", "盐水花生,补气养血",
          "https://gimg2.baidu.com/image_search/src=http://www.kaotop.com/skin/sinaskin/image/nopic.gif"),
      YWHomeProduct("八宝粥", "居家旅行,出门良品",
          "https://gimg2.baidu.com/image_search/src=http://www.kaotop.com/skin/sinaskin/image/nopic.gif")
    ]);
  }
}

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 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 {
  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 {
  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 {
  _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
    疑问:意义及用法?

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5685687.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存