美文网首页
Flutter学习(五)开发初体验

Flutter学习(五)开发初体验

作者: yanhooIT | 来源:发表于2020-04-01 22:11 被阅读0次

    内部团队学习,以简单直接为主

    创建Flutter项目

    • Flutter的项目名称不可以使用驼峰命名,可以使用下划线区分
    • 通过终端命令
    cd 指定目录
    // 创建Flutter项目
    flutter create hello_flutter
    
    • 通过Android Studio创建 通过Android Studio创建

    项目文件介绍

    工程目录
    • .dart_tool:记录依赖库的信息
    • .idea:Android Studio是基于IDEA开发的
    • android:安卓工程
    • ios:iOS工程
    • lib:放我们编写的Flutter代码,里面默认有一个main.dart,它是我们Flutter启动的入口文件,里面有main函数
    • test:UnitTest
    • .gitignore:git忽略文件管理
    • .metadata:对Flutter版本做了记录,不要改动
    • .packages:依赖库生成文件
    • hello_flutter.iml:记录配置信息
    • pubspec.lock:依赖库生成文件
    • pubspec.yaml:设置flutter工程的依赖库
    • README.md:项目描述文件

    runApp和Widget

    // 启动App
    void main() => runApp(MyApp());
    
    /// 是Flutter内部提供的一个函数
    /// 当我们启动一个Flutter应用程序时就是从调用这个函数开始的
    /// 入参是一个Widget
    void runApp(Widget app) {
      // 省略代码...
    }
    
    • runApp是Flutter内部提供的一个函数,启动App的入口,这个函数的入参是一个Widget
    • Widget可以理解为小部件,Flutter中万物皆Widget
      • 在我们iOS或者Android开发中,我们的界面有很多种类的划分:应用(Application)、视图控制器(View Controller)、活动(Activity)、View(视图)、Button(按钮)等等,在Flutter里这些东西都是不同的Widget而已
      • 整个应用程序中所看到的内容几乎都是Widget,甚至是内边距的设置,我们也需要使用一个叫Padding的Widget来做
    • 导入Flutter默认已经给我们提供的material库,来使用其中的很多内置Widget
    import 'package:flutter/material.dart';
    

    material设计风格

    • material是Google公司推行的一套设计风格,或者叫设计语言设计规范等;
    • 里面有非常多的设计规范,eg:颜色、文字的排版、响应动画与过度、填充等等,在Flutter中高度集成了material风格的Widget
    • 在应用中,可以直接使用这些内置Widget来创建应用

    自定义Widget来解决Flutter嵌套过多的问题

    • 在Flutter开发中,我们可以继承自StatelessWidget或者StatefulWidget来创建自己的Widget类;
      • StatelessWidget:没有数据改变的Widget,适用于做一些纯展示工作;
      • StatefulWidget:有数据改变的Widget,适用于有交互的复杂场景;
    • 这里我们先使用StatelessWidget来自定义Widget
      • 让自己创建的Widget继承自StatelessWidget;
      • StatelessWidget包含一个必须重写的方法:build方法;
    class MyStatelessWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return <返回我们的Widget要渲染的Widget,比如一个Text Widget>;
      }
    }
    
    • build方法什么情况下被执行呢?
      • 当我们的StatelessWidget第一次被插入到Widget树中时(也就是第一次被创建时)
      • 当我们的父Widget(parent widget)发生改变时,子Widget也会被重新构建
      • 如果我们的Widget依赖InheritedWidget的一些数据,InheritedWidget数据发生改变时

    为什么定义到Widget中的数据一定是不可变的,需要使用final来修饰

    • Flutter就是这么设计的:一旦Widget中展示的数据发生变化,就重新构建整个Widget
    • Flutter中通过@immutable关键字来给类添加注解来限定定义到Widget中的数据一定是不可变的,所以成员变量需要使用final来修饰(这里涉及到Dart的元编程,后面再说),如下所示:
    @immutable
    abstract class Widget extends DiagnosticableTree {
        // 省略代码...
    }
    

    StatelessWidget

    • Android Studio的智能提示是区分大小写的,如:MaterialApp,如果打materialApp是不能正确进行智能提示的
    • StatelessWidget综合示例,注意看代码注释
    /// 导入Flutter提供的Material库,可以使用其中很多内置Widget
    import 'package:flutter/material.dart';
    
    /// runApp是Flutter内部提供的一个函数
    /// 当我们启动一个Flutter应用程序时就是从调用这个函数开始的
    /// 入参是一个Widget
    void main() => runApp(MyApp());
    
    /// 自定义Widget来减少嵌套层级
    class MyApp extends StatelessWidget {
      /// 继承自StatelessWidget必须实现此方法
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          /// 去除debug标记
          debugShowCheckedModeBanner: false,
          /// 设置主题
          theme: ThemeData(primaryColor: Colors.yellowAccent),
          /// Scaffold是脚手架
          home: Scaffold(
            /// 导航栏
            appBar: AppBar(
              title: Text("火信"),
            ),
            /// body是页面的内容部分
            body: HomeView(),
          ),
        );
      }
    }
    
    /// 自定义Widget - 首页Widget
    class HomeView extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        /// 界面整体边距
        return Padding(
          /// padding属性用于设置边距大小
          padding: const EdgeInsets.all(8.0),
          /// 如果这里使用Column会出错
          /// 因为在Flutter的布局中,内容是不能超出屏幕范围的,当超出时不会自动变成滚动效果,而是会报下面的错误
          ///
          /// 解决方案:Column换成ListView
          /// ListView可以让自己的子Widget变成滚动的效果
          child: ListView(
            children: <Widget>[
              ProductCell("火信1.0", "来火信,聊懂区块链",
                  "https://bkimg.cdn.bcebos.com/pic/09fa513d269759ee3c61708ebffb43166d22df45?x-bce-process=image/watermark,g_7,image_d2F0ZXIvYmFpa2U4MA==,xp_5,yp_5"),
              ProductCell("火信2.0", "来火信,聊懂区块链",
                  "https://bkimg.cdn.bcebos.com/pic/09fa513d269759ee3c61708ebffb43166d22df45?x-bce-process=image/watermark,g_7,image_d2F0ZXIvYmFpa2U4MA==,xp_5,yp_5"),
              ProductCell("火信3.0", "来火信,聊懂区块链",
                  "https://bkimg.cdn.bcebos.com/pic/09fa513d269759ee3c61708ebffb43166d22df45?x-bce-process=image/watermark,g_7,image_d2F0ZXIvYmFpa2U4MA==,xp_5,yp_5"),
            ],
          ),
        );
      }
    }
    
    /// 自定义Widget - 单个Cell Widget
    class ProductCell extends StatelessWidget {
      final String title;
      final String desc;
      final String imageUrl;
      
      /// 构造函数
      ProductCell(this.title, this.desc, this.imageUrl);
    
      @override
      Widget build(BuildContext context) {
        /// Container用于设置内边距和边框
        return Container(
          padding: EdgeInsets.all(20),
          /// 通过decoration来设置边框
          decoration: BoxDecoration(border: Border.all()),
          child: Column(
            children: <Widget>[
              /// Text文本Widget
              Text(title, style: TextStyle(fontSize: 24)),
              Text(desc, style: TextStyle(fontSize: 18)),
              /// 文字图片的间距
              SizedBox(
                /// 设置一个height属性,可以增加一些距离
                height: 10,
              ),
              /// Image是图片Widget
              Image.network(imageUrl)
            ],
          ),
        );
      }
    }
    
    • 运行效果 火信

    StatefulWidget

    • Flutter将StatefulWidget设计成了两个类StatefulWidgetState
    • 一个类继承自StatefulWidget,作为Widget树的一部分
    • 一个类继承自State,用于记录变化的状态,并且根据状态的变化,构建出新的Widget
    • 之所以这样设计,是因为在Flutter中,只要数据改变了就需要rebuild Widget
    • StatefulWidget的综合示例,注意看代码注释
    /// 导入Flutter提供的Material库,可以使用其中很多内置Widget
    import 'package:flutter/material.dart';
    
    main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        /// MaterialApp提示区分大小写
        return MaterialApp(
          /// 去除debug标记
    //      debugShowCheckedModeBanner: false,
          /// 设置主题
          theme: ThemeData(primaryColor: Colors.yellowAccent),
          home: HomeWidget(),
        );
      }
    }
    
    class HomeWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        /// Scaffold是脚手架,区分大小写
        return Scaffold(
          /// Scaffold提示区分大小写
          appBar: AppBar(
            title: Text("HuobiChat"),
          ),
          body: HomeView("你好,火信!"),
        );
      }
    }
    
    /// Widget是不加_: 暴露给外部使用
    class HomeView extends StatefulWidget {
      final String message;
    
      HomeView(this.message);
    
      @override
      State<StatefulWidget> createState() {
        return _HomeViewState();
      }
    }
    
    /// 为什么Flutter在设计的时候StatefulWidget的build方法放在State中
    ///   1.build出来的Widget是需要依赖State中的变量(状态/数据)
    ///   2.在Flutter的运行过程中:
    ///     Widget是不断的销毁和创建的
    ///     当我们自己的状态发生改变时, 并不希望重新状态一个新的State
    
    /// State是加_: 这样状态这个类只是给StatefulWidget内部使用
    class _HomeViewState extends State<HomeView> {
      int _counter = 0;
    
      @override
      Widget build(BuildContext context) {
        /// 居中Widget
        return Center(
          /// Column小部件:当有垂直方向布局时,就使用它;
          child: Column(
            /// 居中
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              _getButtons(),
              Text(
                "当前计数:$_counter",
                style: TextStyle(fontSize: 25),
              ),
              Text("传递的信息:${widget.message}")
            ],
          ),
        );
      }
    
      Widget _getButtons() {
        /// Row小部件:当时水平方向布局时,就使用它
        return Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            /// RaiseButton小部件:可以创建一个按钮,并且其中有一个onPressed属性是传入一个回调函数,当点击按钮时回调
            RaisedButton(
              child: Text(
                "+",
                style: TextStyle(fontSize: 20, color: Colors.white),
              ),
              color: Colors.pink,
              onPressed: () {
                /// 手动调用setState方法,会根据最新的状态(数据)来重新调用build方法,构建对应的Widgets
                setState(() {
                  _counter++;
                });
              },
            ),
            RaisedButton(
                child: Text(
                  "-",
                  style: TextStyle(fontSize: 20, color: Colors.white),
                ),
                color: Colors.purple,
                onPressed: () {
                  setState(() {
                    _counter--;
                  });
                }),
          ],
        );
      }
    }
    
    • 运行效果


      HuobiChat

    生命周期

    StatelessWidget生命周期
    • 代码演示
    import 'package:flutter/material.dart';
    
    main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: HomeWidget(),
        );
      }
    }
    
    class HomeWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return HomeView("1111");
      }
    }
    
    // StatelessWidget的生命周期
    class HomeView extends StatelessWidget {
      HomeView() {
        print("1---构造函数被调用");
      }
    
      @override
      Widget build(BuildContext context) {
        print("2---调用build方法");
        return Text("111");
      }
    }
    
    • 打印结果
    flutter: 1---构造函数被调用
    flutter: 2---调用build方法
    
    StatefulWidget生命周期
    • 生命周期图示 StatefulWidget生命周期
    • 代码演示

    import 'package:flutter/material.dart';
    
    main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: HomeWidget(),
        );
      }
    }
    
    class HomeWidget extends StatefulWidget {
      @override
      _HomeWidgetState createState() => _HomeWidgetState();
    }
    
    class _HomeWidgetState extends State<HomeWidget> {
      @override
      Widget build(BuildContext context) {
        /// Scaffold是脚手架,区分大小写
        return Scaffold(
          /// Scaffold提示区分大小写
          appBar: AppBar(
            title: Text("StatefulWidget的生命周期"),
          ),
          body: HomeView(),
          floatingActionButton: RaisedButton(
            child: Icon(Icons.add),
            /// 点击这里会让HomeView重新构建,这时就会触发HomeViewState的didUpdateWidget方法
            onPressed: () {
              setState(() {
                print("rebuid Widget");
              });
            },
          ),
        );
      }
    }
    
    // StatefulWidget的生命周期
    class HomeView extends StatefulWidget {
      HomeView() {
        print("1---调用HomeView的constructor方法");
      }
    
      @override
      _HomeViewState createState() {
        print("2---调用HomeView的createState方法");
        return _HomeViewState();
      }
    }
    
    class _HomeViewState extends State<HomeView> {
      int _counter = 0;
    
      _HomeViewState() {
        print("3---调用_HomeViewState的constructor方法");
      }
    
      /// 执行initState,我们通常会在这个方法中执行一些数据初始化的操作,或者也可能会发送网络请求
      /// 这个方法是重写父类的方法,必须调用super,因为父类中会进行一些其他操作;
      /// initState上有一个注解(annotation):@mustCallSuper
      @override
      void initState() {
        // 调用: 这里是必须调用super
        final TextStyle style = TextStyle();
    
        super.initState();
        print("4---调用_HomeViewState的initState方法");
      }
    
      /// 执行didChangeDependencies方法,这个方法在两种情况下会调用
      ///   情况一:调用initState会调用;
      ///   情况二:从其他对象中依赖一些数据发生改变时,比如前面我们提到的InheritedWidget(这个后面会讲到);
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        print("5---调用_HomeViewState的didChangeDependencies方法");
      }
    
      /// 执行build方法,来看一下我们当前的Widget需要渲染哪些Widget
      @override
      Widget build(BuildContext context) {
        print("6---调用_HomeViewState的build方法");
        return Column(
          children: <Widget>[
            RaisedButton(
              child: Icon(Icons.add),
              onPressed: () {
                setState(() {
                  _counter++;
                });
              },
            ),
            Text("数字:$_counter")
          ],
        );
      }
    
      /// 当前的Widget不再使用时,会调用dispose进行销毁
      @override
      void dispose() {
        print("---调用_HomeViewState的dispose方法");
        super.dispose();
      }
    
      /// 执行didUpdateWidget方法是在当父Widget触发重建(rebuild)时,系统会重新构建Widget时会调用didUpdateWidget方法
      @override
      void didUpdateWidget(HomeView oldWidget) {
        super.didUpdateWidget(oldWidget);
        print("---didUpdateWidget");
      }
    }
    
    • 打印结果
    flutter: 1---调用HomeView的constructor方法
    flutter: 2---调用HomeView的createState方法
    flutter: 3---调用_HomeViewState的constructor方法
    flutter: 4---调用_HomeViewState的initState方法
    flutter: 5---调用_HomeViewState的didChangeDependencies方法
    flutter: 6---调用_HomeViewState的build方法
    
    // 这里是Android Studio里才会打印,VSCode里就不会,具体原因不详
    flutter: 1---调用HomeView的constructor方法
    flutter: ---didUpdateWidget
    flutter: 6---调用_HomeViewState的build方法
    
    • 当点击右下角的加号按钮时,会手动执行setState方法后会打印如下结果
    flutter: rebuid Widget
    flutter: 1---调用HomeView的constructor方法
    flutter: ---didUpdateWidget
    flutter: 5---调用_HomeViewState的build方法
    

    命令式编程 和 声明式编程

    • 命令式编程:命令式编程非常好理解,就是一步步给计算机命令,告诉它我们想做什么事情;
    • 声明式编程:声明式编程通常是描述目标的性质,你应该是什么样的,依赖哪些状态,并且当依赖的状态发生改变时,我们通过某些方式通知目标作出相应;
    • 目前在VueReact、包括iOS中的SwiftUI中以及Flutter目前都采用了声明式编程

    相关文章

      网友评论

          本文标题:Flutter学习(五)开发初体验

          本文链接:https://www.haomeiwen.com/subject/wjvhuhtx.html