美文网首页
Flutter(1)-初探Flutter,感受Dart

Flutter(1)-初探Flutter,感受Dart

作者: BoxJing | 来源:发表于2019-09-26 09:12 被阅读0次

    RN想必大家都知道,它的技术底层是沿用了native的技术,渲染就是用原始的渲染的,所以会导致安卓和iOS的兼容性不太好,组件会分平台,不好维护,连忠实的追随者Airbnb也不得不放弃了,改为最初的原生开发了。
    Flutter,2015年10月就有了,2018年12月正式版本1.0发布,是谷歌开发的,渲染引擎是自己用C++搞出来的,不再借用原生的。个别地方iOS和安卓的处理方式不一样,但是这个不一样在开发者这层是体现不出来的,代码只需要一套,所以在兼容性上就非常的好。
    本篇文章开始慢慢的探究一下Flutter。环境的配置大家去Flutter官网Flutter中文网探索安装就可以了。

    1.初次接触

    本篇开始带着大家一起慢慢的学习Flutter,开发工具使用的是Android Studio,当然你用VS Code也可以。cd进入到对应的文件夹然后命令行flutter create boxdemo创建一个boxdemo的flutter工程,flutter的命令这里也不再赘述,良心话,真正开发的过程中像这种创建工程、运行工程的有可视化的如果还用命令行那就是纯属装逼,哈哈。Android Studio的flutter插件安装过了的话直接就直接可以在界面上点击New Flutter Project,按照步骤一步一步即可创建flutter工程,创建完后如果发现无法在iOS模拟器或者真机上运行,请用xcode打开ios目录下的Runner.xcworkspace进行证书设置,然后就可以正常运行了。


    优先来一个Flutter Application工程。用Android Studio运行的过程中如果出现意外中断后,再也运行不起来或者运行的不是最新代码,请在终端执行rm ~/flutter/bin/cache/lockfile删除flutter/bin里面的缓存区就可以了,flutter的路径请按照自己电脑上的路径我这里就是放在~中。Flutter主要的语言是dart,在后面的写代码过程中我们会一直用到它,新建完工程后,我们从main.dart入手,这里是flutter工程的起始点,就像是一个iOS工程的AppDelegate一样,会发现main.dart有一个引入 import 'package:flutter/material.dart';,这个引入在以后的日子你会经常看到,类似于iOS中的UIKit,是谷歌给大家提供的一套UI方案。看着初始工程的main.dart里面代码有点多,全部干掉,来一个简单的代码:
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(
          Center(
            child: Text('BoxJing')
          )
      );
    }
    

    这代码足够简单了,就是一个简单的在中间位置显示一个BoxJing字符串。
    在Flutter工程中会有一个Widget(小部件)我们会频率很高的见到它,这个东西就是我们在iOS里面的控件。Widget分为下面两种:

    • stateless: 无状态,死板板的显示,初始化后无法再改变
    • stateful: 有状态,可以根据需要刷新

    那么很明显,我们可以根据自己的需要来封装不同的Widget,那么先来个我们自己封装一个简单的statelessWidget:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(
          BoxWidgetLess()
      );
    }
    
    class BoxWidgetLess extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        // TODO: implement build
        return Center(
            child: Text('一个阿狸', textDirection: TextDirection.ltr)
        );
      }
    }
    

    如果某个函数里只有一句代码的时候,比如上面main函数,我们可以直接简化为:void main() => runApp(BoxWidgetLess());。既然我们用到了Text这个Widget,下面就仔细的说一下这个Text:

    class Text extends StatelessWidget {
      /// The [data] parameter must not be null.
      const Text(
        this.data, {
        Key key,
        this.style,
        this.strutStyle,
        this.textAlign,
        this.textDirection,
        this.locale,
        this.softWrap,
        this.overflow,
        this.textScaleFactor,
        this.maxLines,
        this.semanticsLabel,
        this.textWidthBasis,
      }) : assert(
             data != null,
             'A non-null String must be provided to a Text widget.',
           ),
           textSpan = null,
           super(key: key)
    
      final TextStyle style;
      ...
      final TextAlign textAlign;
    

    也就是说第一个参数data必须传值,后面大括号里面的全是可选参数,来个实实在在的例子,文字带大小和颜色最基本的操作:

    class BoxWidgetLess extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        // TODO: implement build
        final _boxTxtStyle = TextStyle(
          color: Colors.blue,
          fontSize: 36.0,
          fontWeight: FontWeight.bold
        );
        return Center(
            child: Text('一个阿狸',
                textDirection: TextDirection.ltr,
                style: _boxTxtStyle
            )
        );
      }
    }
    

    还是通俗易懂的,多练习练习就熟练了。下面来个一直想要的界面效果:

    void main() => runApp(BoxApp());
    class BoxApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
    //      debugShowCheckedModeBanner: false, //隐藏右上角DEBUG标识
          home: Scaffold(
            appBar: AppBar(
              title: Text('Box Demo'),
            ),
            body: BoxWidgetLess(),
          ),
          theme: ThemeData(
            primaryColor: Colors.purple
          ),
        );
      }
    }
    

    前后对比效果来一个:


    这个MaterialApp其实就是系统帮我们封装了一个可以让我们快速开发的手脚架,点进去源码,解释的也很清楚,Scaffold就像是iOS开发中的UINavigationController,实际上比它要弱,可以暂时的这样理解起来,appBar就是导航条,body就是导航条下面的空白部分,theme可以用来设置一些主题颜色,其他很多小东西大家就自己试一试。

    2. ListView

    我们在开发中用到的最多的ListView,那必须来一波操作,先来个简单的代码:

    void main() => runApp(BoxHome());
    class BoxApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: BoxHome(),
          theme: ThemeData(
              primaryColor: Colors.blue
          ),
        );
      }
    }
    class BoxHome extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Box Demo'),
          ),
          body: ListView.builder(
              itemCount: 10,
              itemBuilder: _boxCellForRow,
          ),
        );
      }
    // cellForRow方法
      Widget _boxCellForRow(BuildContext context, int index) {
        return Text('阿狸');
      }
    }
    

    效果就是返回10行文字,全是阿狸。从代码中可以看到ListView的构建方法使用ListView.builder,里面的参数也是非常好理解的,itemCount就是行数,itemBuilder就是每个cell,其他的参数直接去看源码注释,Flutter中的ListView是没有iOS中TableViewSection的概念的。简单的把cell的构建摘出去一个单独方法来操作了,这里说一点,在Dart中带_的属于内部的变量或者方法,外部不可访问,不带_属于公开的。这里我们为了达到一个更好的效果,新建一个people.dart文件来作为一个model,等下我们会用这个model来填充cell:
    class People {
    // 构造方法
      const People({
        this.name,
        this.avatar
      });
    // 两个属性  final 不可变的  var 可以更改的
      final String name;
      final String avatar;
    }
    // 这里声明一个公开的数据源 后面外部需要使用填充cell
    final List<People> boxArr = [
      People(name:'阿狸1',avatar: 'http://cache.fotoplace.cc/ap2/190521/0/7B5C54B95100DE281E689EEF24E499D9.png'),
      People(name:'阿狸2',avatar: 'http://cache.fotoplace.cc/ap2/190521/0/6478737E09CA39170389191133953111.png'),
      People(name:'阿狸3',avatar: 'http://cache.fotoplace.cc/ap2/190521/0/5FD190ECFF4943167F3D5F19A2973D6D.png')
    ];
    

    然后在main.dart中引入import 'model/people.dart';并且改掉cellForRow方法:

    // 直接一个图片控件
    Widget _boxCellForRow(BuildContext context, int index) {
        return Container(
          color: Colors.white,
          height: 88.0,
          margin: EdgeInsets.all(6.0),
          child: Image.network(boxArr[index].avatar),
        );
      }
    

    这里就是放一个单独的控件Image来加载网络图片,在开发中很少用到,一般情况下我们都是多个控件在一个cell中,来几个多控件其他布局,可以放开不同的注释看效果,完整的代码可以在文章底部的github上下载运行:

    // cellForRow方法
    ////  横向 Row
    //  Widget _boxCellForRow(BuildContext context, int index) {
    //    return Container(
    //      color: Colors.white,
    //      height: 88.0,
    //      margin: EdgeInsets.all(6.0),
    //      child: Row(
    //        children: <Widget>[
    //          Image.network(boxArr[index].avatar),
    //          Text(boxArr[index].name)
    //        ],
    //      ),
    //    );
    //  }
    ////  竖向 Column
    //  Widget _boxCellForRow(BuildContext context, int index) {
    //    return Container(
    //      color: Colors.white,
    //      height: 240.0,
    //      margin: EdgeInsets.all(6.0),
    //      child: Column(
    //        children: <Widget>[
    //          Image.network(boxArr[index].avatar),
    //          Text(boxArr[index].name)
    //        ],
    //      ),
    //    );
    //  }
    ////  垂直 z轴 Stack
    //  Widget _boxCellForRow(BuildContext context, int index) {
    //    return Container(
    //      color: Colors.white,
    //      height: 240.0,
    //      margin: EdgeInsets.all(6.0),
    //      child: Stack(
    //        children: <Widget>[
    //          Image.network(boxArr[index].avatar),
    //          Text(boxArr[index].name)
    //        ],
    //      ),
    //    );
    //  }
    

    先来看一下这三种效果:


    这三种布局会是我们在整个Flutter开发中使用到,各种各样的布局都是用这三种布局来实现的。一般情况下,我们自定义cell最外层会用一个Container来包一层,顾名思义是一个容器,其实就是一个很干净的部件,在单个控件中我们直接是在child里面放置了一个控件就可以了,多个的时候,一定要有一个布局的方式,所以使用这三个中的其中一个包多个,用了children,这命名相当的人性化吧。这时候,如果想文字和图片中间有点间距怎么办?使用SizedBox来进行间隔设置,就是图片和文字之间放了一个宽度为10pt的空白区域,我代码里使用的是SizedBox(width: 10),如果是竖排方向间隔那就是SizedBox(height: 10),直接上代码:
    //  横向 Row
      Widget _boxCellForRow(BuildContext context, int index) {
        return Container(
          color: Colors.white,
          height: 88.0,
          margin: EdgeInsets.all(6.0),
          child: Row(
            children: <Widget>[
              Image.network(boxArr[index].avatar),
              SizedBox(width: 10),
              Text(boxArr[index].name)
            ],
          ),
        );
      }
    

    margin这个东西如果你接触过CSS,应该就很明白是干嘛用的,就是用来设置边距的,再看后面的赋值EdgeInsets,作为一个iOS开发者很熟悉,经常会用到的设置上下左右的距离,在Flutter中常用的有allonly,all就是直接设置四边距离都是同一个值,only的话就需要指明是哪个方向的边距是多少,还有其他的symmetricfromWindowPadding等方法看看注释,试一试就可以了,看一下运行效果:


    如果想固定这个图片的大小,那就直接给Image设置widthheight来实现,可以直接去掉Container的height了:
    //  横向 Row
      Widget _boxCellForRow(BuildContext context, int index) {
        return Container(
          color: Colors.white,
    //      height: 88.0,
          margin: EdgeInsets.all(6.0),
          child: Row(
            children: <Widget>[
              Image.network(boxArr[index].avatar,width: 60.0,height: 60.0),
              SizedBox(width: 10),
              Text(boxArr[index].name)
            ],
          ),
        );
      }
    

    看一下运行效果:


    如期达到了我们想要的效果。有一点要注意,并不是所有的部件都有widthheight属性的,如果没有,就把它放进一个ContainerContainer是有widthheight属性的。

    本篇就简单的接触一下Flutter,感受一下Dart语言,所有的代码都可以在Github:BoxJ/Flutter-daydayup中下载,本篇代码的文件夹是boxdemo_001,欢迎一起交流!

    相关文章

      网友评论

          本文标题:Flutter(1)-初探Flutter,感受Dart

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