• 开启Fluter基础之旅<一>-------官网DEMO解读、核心概念了解、常用组件学习之container、image


    在2019年底对于Flutter的Dart语言进行了基础的学习,转眼又来到了2020年3月中下旬了,对于Flutter的学习又断了几个月了,今年目标一定得要把它能学透学精通达到能做商业项目的能力,如今在各大招聘JD上对于会Flutter的安卓程序猿都会优先考虑,可见其它的地位是越来越高了,所以接下来脚踏实地的沿着之前打好的基础继续前行。

    官网DEMO解读:

    这里重新创建一个工程,Flutter的学习从默认的官网DEMO熟悉开始:

     

    然后用Android打开运行一下:

    经典的官方默认运行就如上面所示,而这个小小的案例中能看到Flutter的一个大致的开发流程,另外不是之前已经对于Dart的基础语法进行学习了嘛,然后通过这个小DEMO正好来复习一下Dart语法,看我们之前学的各种语法在Flutter系统源码中都是如何体现的,这样就能让我们之前学的枯燥的语法给串活了,所以接下来好好的对官方生成的这个示例代码进行一下大概解读:

    pubspec.yaml:

    对于Flutter工程来说:

    这个是一个很重要的配置文件,先来瞅一瞅它:

     

     

    这里有一个小细节需要注意,由于注释#后面跟了一个空格,而打开注释进行图片配置的时候这块需要注意:

     

    另外有个问题?难道所有的图片都需要在这注册么?是的,但是这里可以直接配置一个文件夹中的图片,也就是:

     

    main.dart:

    这里面的代码目前还看不太懂,没关系,先整体来读一读,多少会有些收获的,先全部贴出来:

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            // This is the theme of your application.
            //
            // Try running your application with "flutter run". You'll see the
            // application has a blue toolbar. Then, without quitting the app, try
            // changing the primarySwatch below to Colors.green and then invoke
            // "hot reload" (press "r" in the console where you ran "flutter run",
            // or simply save your changes to "hot reload" in a Flutter IDE).
            // Notice that the counter didn't reset back to zero; the application
            // is not restarted.
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      // This widget is the home page of your application. It is stateful, meaning
      // that it has a State object (defined below) that contains fields that affect
      // how it looks.
    
      // This class is the configuration for the state. It holds the values (in this
      // case the title) provided by the parent (in this case the App widget) and
      // used by the build method of the State. Fields in a Widget subclass are
      // always marked "final".
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      int _counter = 0;
    
      void _incrementCounter() {
        setState(() {
          // This call to setState tells the Flutter framework that something has
          // changed in this State, which causes it to rerun the build method below
          // so that the display can reflect the updated values. If we changed
          // _counter without calling setState(), then the build method would not be
          // called again, and so nothing would appear to happen.
          _counter++;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        // This method is rerun every time setState is called, for instance as done
        // by the _incrementCounter method above.
        //
        // The Flutter framework has been optimized to make rerunning build methods
        // fast, so that you can just rebuild anything that needs updating rather
        // than having to individually change instances of widgets.
        return Scaffold(
          appBar: AppBar(
            // Here we take the value from the MyHomePage object that was created by
            // the App.build method, and use it to set our appbar title.
            title: Text(widget.title),
          ),
          body: Center(
            // Center is a layout widget. It takes a single child and positions it
            // in the middle of the parent.
            child: Column(
              // Column is also layout widget. It takes a list of children and
              // arranges them vertically. By default, it sizes itself to fit its
              // children horizontally, and tries to be as tall as its parent.
              //
              // Invoke "debug painting" (press "p" in the console, choose the
              // "Toggle Debug Paint" action from the Flutter Inspector in Android
              // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
              // to see the wireframe for each widget.
              //
              // Column has various properties to control how it sizes itself and
              // how it positions its children. Here we use mainAxisAlignment to
              // center the children vertically; the main axis here is the vertical
              // axis because Columns are vertical (the cross axis would be
              // horizontal).
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'You have pushed the button this many times:',
                ),
                Text(
                  '$_counter',
                  style: Theme.of(context).textTheme.display1,
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ), // This trailing comma makes auto-formatting nicer for build methods.
        );
      }
    }

    接下来走读一下:

    那哪些都用了这个库呢,咱们把这个导包的语句去掉看报错就晓得了:

    继续往下分析:

    所以这里面就定义了一个这样的Widget类:

     

    无状态的Widget是个啥呢?这里官方有一句话来描述了:

    而这个无状态的Widget里面有个build()方法,一看就是用来进行构建的:

    直接返回它,那来看一下这个MaterialApp类:

    它当然是承载可变的元素喽,比如说图片的显示,文字的变化,点击事件的响应之类的,基本上跟用户有交互的都要用这个,所以此时则构建了一个Material风格的Widget了,来看一下它的构建细节:

    这里就正好可以复习一下我们之前几次学的Dart语法啦,先看一下MaterialApp的构造定义:

    这是啥构造函数呀,其实它叫常量构造,可以到这https://www.cnblogs.com/webor2006/p/11981709.html回忆一下:

    那它有啥好处呢?

    嗯,然后这个构造中的参数是定义在了一对花括号当中了:

    这又是什么语法呢?其实是叫可选命名参数,这里可以在这https://www.cnblogs.com/webor2006/p/11975291.html回忆一下Dart语法了:

    它有一个注意点就是参数调用时一定得要显示指定参数名,像这样调用是不行的:

    哦,原来我们之前学的枯燥的语法最终都是能派上用场的嘛,所以,此时对于这块的写法就不陌生了:

    其中这个title是不生效的,因为由home中的MyHomePage进行了设置了,然后theme则是主题,看一下ThemeData是个啥?

    其实它叫做工厂构造,复习一下:

    有啥用也可以看到了,是一个单例对象,好,再来分析最后一个参数:

    先看一下这个参数接收的是个什么类型:

    所以此时又定义了一个类,不过这次是一个有状态的,因为准备要构建带变化的视图了:

    其中有一个createState()方法,关于这块之后还会讲,这里理解系统会调用它,然后我们需要定义一个State类:

     其中它里面的代码可能看着很怪,因为缺少一些理论的东东,这里就简单看一下便好:

    它里面有一个Build()方法,可以理解成Android里面View的onDraw(),所以里面直接用了一个系统库中的Widget:

    它的中文意思是脚手架,也就是页面的骨架。

    然后此时就要以看到视图的构建细节了,也就是最终运行所看到的,首先是一个AppBar:

    也就是它:

    看一下AppBar是个什么东东:

    其中有一个比较奇怪的,就是它的标题的设置:

    那这个Text又是啥呢?

    往下再分析:

    也就是:

    它用了一个Center来构建的,我猜它肯定又是一个Widget:

    很明显,这个Widget的作用是为了将里面的元素居中显示:

    然后它里面的孩子是一个列布局,有点像Android里面的LinearLayout的垂直布局:

    此Column肯定又是Widget啦:

    然后它的第一个参数是控制主轴方向的:

    也就是y轴方向,很显然咱们的孩子就是上下居中的:

    那横轴怎么控制呢?这里可以用这个属性:

    此时效果就发生变化了:

    而看到的两个文本则是由这块来指定的:

     

    其中这里面的写法好奇怪,先来看一下children是啥类型:

    它的构建确实就是可以放到[]中来构建的,所以也不足为奇,其中第二个次数显示的则使用了一个表达式,动态的来根据实际count的数量来进行变化:

    好,最后一个元素则是那个点击按钮了:

    它当然也是一个Widget喽:

    第一个参数为onPressed,它其实是一个点击事件的回调方法:

    所以这里就指向了count++那个函数了:

    然后第一个参数指定的是一个tooltip,其实它就是长按之后的提示:

    最后一个则为按钮的图标,由系统提供的,决结一下此DEMO的一个视图树的层次结构:

     

    其中对于Flutter来说,记住一句话,一切皆是Widget,这个从上面的DEMO分析就可以看到,而在之前我们学习Dart语言时也有一句话,一句皆是对象!!虽说目前还没有手动编写过Flutter的代码,但是通过细品这官方的代码,也能或多或少的感受到它的一个编写规则,还是很有意义的。

    核心概念了解:

    上面在分析官方代码时,是不是感觉还是有些吃力,比如说:

    要搞清楚这些东东,其实还是需要有一些理论基础才行的,所以下面来了解一些理论化的概念,有助于更好的来学习Flutter。

    视图树:

    这块就跟Android完全不一样了,稍稍有点不好理解,但是相当的重要,先来看一下图:

    如上图所示,视图树是包含三种,一种是Widget树,第二种是Element树,第三种是Render树,其整个过程如下:

    创建widget树。调用runApp(rootWidget),将rootWidget传给rootElement,做为rootElement的子节点,生成Element树,由Element树生成Render树,Render树的根是一个RenderView。也就是说我们代码中编写的Widget它是不负责渲染的,最终渲染是得靠Render树,而且每个树结点都会有一个对应的转换结点: 

    而对于这三者视图树都发挥啥作用呢?下面描述一下:

    • Widget:存放渲染内容、视图布局信息,widget的属性最好都是immutable。
    • Element:存放上下文,通过Element遍历视图树,Element同时持有Widget和RenderObject。
    • RenderObject:根据Widget的布局属性进行layout,对widget传入的内容进行渲染绘制。

    这块需要好好感受一下,跟Android的不同。

    树更新:

    • 全局更新:调用runApp(rootWidget),一般flutter启动时调用后不会再调用。
      如咱们程序所示:
    • 局部子树更新:将子树作StatefulWidget的一个子Widget,并创建对应的State类实例,通过调用State.setState()触发子树的刷新。
      有这条做为理解依据,所以官方DEMO的这个代码就可以理解了:

      也就是当我们点击按钮时则会触发_counter++这个动作,而这个动作最终要让TextView进行刷新就必须将这个++放到setState()方法当中了。

    StatelessWidget:

    这个是指无状态的Widget:

    看一下它的理论化描述:

    • StatelessWidget:无中间状态变化的widget,初始状态设置以后就不可再变化。

    • StatelessWidget用于不需要维护组件状态的场景。

    • createElement()创建StatelessElement对象。

    • 一个StatelessWidget对应一个StatelessElement。

    提示:Flutter推荐尽量使用StatelessElement,性能好,但是吧也得看实际情况。

    StatefulWidget:

    看一下它理论化的描述:

    • StatefulWidget:存在中间状态变化的widget。
    • createElement()创建StatelfulElement对象。
    • createState()创建State对象(可能调用多次)。
    • 一个StatefulElement对应一个State实例。

    所以Demo中就有这样的代码:

    当一个StatefulWidget同时插入到widget树的多个位置时,Flutter framework就会调用createState方法为每一个位置生成一个独立的State实例。

    State:

    它有两个重要的属性:

    • widget:表示与该State实例关联的widget实例。
      也就是:

    • context:BuildContext的一个实例。

    另外关于Widget、State、setState()、BuildContext之间的关系,描述如下:

    • 可以手动调用其setState()方法通知Flutter framework状态发生改变,Flutter framework在收到消息后,会重新调用其build方法重新构建widget树,从而达到更新UI的目的。
    • Widget与State的关联不是永久的。UI树上的某一个节点的widget实例在重新构建时可能会变化,但State实例只会在第一次插入到树中时被创建,当在重新构建时,如果widget被修改了,Flutter framework会动态设置State.widget为新的widget实例。
    • BuildContext表示构建widget的上下文,它是操作widget在树中位置的一个句柄,它包含了一些查找、遍历当前Widget树的一些方法。每一个widget都有一个自己的context对象。

    State生命周期【重要】:

    任何关于生命周期的知识都是非常重要的,所以下面好好的来看一下,这里分为三个维度来看:

    StatefulWidget插入到widget树:

    initState:当Widget第一次插入到Widget树时会被调用,对于每一个State对象,Flutter framework只会调用一次该回调,所以,通常在该回调中做一些一次性的操作,如状态初始化、订阅子树的事件通知等。

    didChangeDependencies:initState后立刻调用,state依赖的对象发生变化时调用。
    build:构建Widget时调用,调用后控件会显示。

    点击⚡️按钮热重载:

    reassemble:此回调是专门为了开发调试而提供的,在热重载时会被调用,此回调在Release模式下永远不会被调用。
    didUpdateWidget:组件状态改变时候调用,可能会调用多次。

    从widget中移除StatefulWidget:

    deactive:当State对象从树中被移除时,会调用此回调。
    dispose:当State对象从树中被永久移除时调用,通常在此回调中释放资源。 

    常用组件学习之container、image:

    好,在详读了官方的默认DEMO的代码之后,接下来则手动来撸码进行操练了,还记得之前说过Flutte里一切皆组件么?所以这里先从一些学用的组件(Widget)开始学起,就像Android里面的图片,文件,列表,滑动之类的,虽说也有点小枯燥,但是这是打好基础的必经之路。

    container: 

    容器组件Container包含一个子widget,自身具备alignment、padding等属性,方便布局过程中摆放child。

    下面新建一个新的文件来进行代码的编写:

    这里就直接校仿官网的写法,其中这里有个技巧,就是对于这个StatelessWidget类要自己定义稍麻烦,其实是可以有快捷键快速的生成模板的,如下:

    这里来试一下效果:

    好,继续,这里先来简单显示一下:

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(
              title: Text('Container示例'),
            ),
          ),
        );
      }
    }

    这里就直接先弄了个脚手架,之前官网的DMEO也是一样的,只是比这要稍微麻烦一点,运行看下效果:

     

    其中右上角有一个DEBUG的标识:

    其实它是可以去掉的,这样弄:

     

    此时点一下则就消失了:

    这里是要学习Container的用法,那接下来内容部分就用一下它:

    运行:

    Center这个Widget就是它里面的元素是一个居中布局的,然后里面有个Container组件,这里再次强调一下,为啥代码可以这样写,毕境是刚入门学习节奏可以慢一点,重点是要搞懂:

    这是因为Container的命名构造有一个child参数,它接收的类型是Widget:

    所以这里就靠熟练生巧了,对于组件的学习也是非常之必要的,接下来继续,貌似这个Container的文字太小了,改变一下它的大小:

    改完之后,Flutter有一个非常好的功能就是热重载,就像前端开发人员一样,当然在Android中也类似的热重载功能,比如Instant run,但是。。貌似不太好用,而在Flutter上相当好用,像上面改了代码要生效,按control+s等个一两秒立马就能看到效果了,反正我在平常搞Android开发中对于更改是比较痛苦的,基本都得等个几分钟才能运行

    设置Container背景色:

    好。接下来咱们来改一下Container的颜色:

    设置Container宽高和Padding:

    设置Container的decoration:

    设置边框:

    它是干嘛的?下面直接看效果既可:

    运行,发现报错了:

    也就是说这里的color冲突了,这里就得注意了:

    运行:

    设置圆角:

    其中这里又得复习一下Dart语法了:

    还记得这是啥语法么?这里回忆一下:

    查看一下它们的定义是否符合命名构造的语法:

     

    嗯,确实是,另外为啥要用const来修饰呢?这里还记得const的好处了么?回忆一下:

    transform:设置Container的变换矩阵,类型为Matrix4

    关于Container的具体用法之后再挖掘。

    image:图片组件

    这再建一个Dart文件来学习一下图片组件。

     

    加载网络图片:

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(
              title: Text("Image示例"),
            ),
            body: Center(
              child: Column(
                children: <Widget>[
                  Image.network(
                    "https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3034071742,1954908597&fm=26&gp=0.jpg",
                     100.0,
                    height: 100.0,
                  )
                ],
              ),
            ),
          ),
        );
      }
    }

    运行:

    其中看一下Image的网络的命名构造的定义:

    加载assets资源图片:

    先定义一张assets的图片:

    运行:

    另外这里还有一个能配置UI调试的东东:

    加载Uint8List内存资源图片:

    这里先要将图片转成Uint8List类型,这里打算采用一个有状态的Widget来实现,先定义一个有状态的Widget:

    其中initState()就派上用场了,这里回忆一下它的生命周期:

     

    另外还用了一个条件判断mouted这又是啥意思呢?

    另外还用到了then来监听数据加载完,这块的语法也复习一下:

    数据加载好了,接下来则需要更新到图片视图中,也就是处理build()方法:

    接下来将其定义到Column中:

    运行一下:

    从相册中选图片进行加载:

    这里还是先来搭建一个有状态的Widget:

     

    然后添加到Column中来瞅一下:

    运行:

    此时咱们点击“选择图片”时,则需要跳到相册中来进行图片的选择,该如何做呢?这里需要用到一个image_picker三方开源库,所以咱们集成一下:

    好,接下来咱们则可以来实现这个点击事件了:

    其中getImage方法使用了async await异步处理方式,这个在之前Dart也学过了。

    运行:

     

    那在IOS上的表现如何呢,运行一下,发现报错了。。

    貌似是ios找不到我们的这个图片相册选择库,这里可以用终端进入到ios目录执行一下这个命令,然后会下载相关的依赖包:

    然后再运行:

  • 相关阅读:
    简而言之C语言:“char类型省空间”只是一个传说
    原来曾经有人支持过我,感动!
    编程的“武林秘籍”
    没有一种语言解决所有问题
    简而言之C语言:const声明
    关于代码的些许感想
    树莓派上搭建arduino命令行开发环境
    IOT设备的7大安全问题
    OsmocomBB软件实现栈概况
    Win7以上 32/64位系统隐藏托盘图标
  • 原文地址:https://www.cnblogs.com/webor2006/p/11996535.html
Copyright © 2020-2023  润新知