美文网首页Flutter
Flutter基本控件和页面布局

Flutter基本控件和页面布局

作者: 许久以前 | 来源:发表于2019-06-19 11:20 被阅读0次

    Flutter开发环境搭建好后,就可以开始开发了。

    有状态组件与无状态组件

    在Flutter 中,一切皆组件:Widget。组件Widget又分为两种:

    1.StatefulWidget(有状态组件):如果一个控件,它需要随着用户交互或者内部的值、状态需要根据不同的外部业务变化而改变的话,那么这个组件就需要被设计为有状态的。比如TextField输入随着输入变化而变化。

    2.StatelessWidget(无状态组件):对比有状态的组件,无状态的组件是不变的,不需要随着业务变化而变化,比如一个固定的Text文本展示控件,他是不需要随着用户交互而改变的,我们把它定为无状态的。

    在Flutter中,没有Controller和Activity的控制器的概念,所有的page也都是一个Widget. 一个页面基本上都是有交互的,所以,一般我们新建一个page都是继承:StatefulWidget,示例代码:

    class CommonWebPage extends StatefulWidget {
      final String url;
      //添加构造函数
      CommonWebPage({this.url});
    
      @override
      State<StatefulWidget> createState() {
        return _CommonWebPageState();
      }
      
    }
    

    上面例子中,我们新建了一个名为CommonWebPage的页面,由于它是由状态的,所以必须要复写他的状态方法:createState(),这个状态方法返回的是专门管理这个page的状态类:_CommonWebPageState。
    那么这个状态类又是什么呢?下面代码就是这个状态类:

    class _CommonWebPageState extends State<CommonWebPage> {
      @override
      Widget build(BuildContext context) {
        return new WebviewScaffold(
          url: widget.url,
          appBar: _navigationBar(),
        );
      }
    }
    

    上面代码中,这个状态类继承State,由于这个状态类是为CommonWebPage页面服务的,所以泛型为这个页面。这些是dart语法,前期可以先不用关注dart的语法,先记住这个固定的写法:

    先创建一个继承StatefulWidget的页面类,然后在创建一个继承State<page类>,然后复写build()方法。

    这个build()其实就是这个页面里面的布局组件了。bulid里面布局的控件即渲染出来的页面样式。

    页面状态变化更新机制

    前面讲了有状态组件,你肯定会想知道,既然是有状态交互变化,那么状态值变化怎么让页面更新的呢?这里就要说一下setState()。

    setState()

    Flutter通过setState来重新渲染页面,也就是说:如果代码执行了setState()方法,那么build()会重新执行一遍,build()里面会根据最新的页面显示重新渲染新的页面。【其实这套机制和RN是一样,如果你以前了解过ReactNative的话】。

    现在我们举个例子说明:
    下图是要给webpage页面加载h5的过程,当页面在加载过程中,中间标题为空,当加载结束后,导航栏上显示标题。

    加载中 加载完成

    我们首先需要定义一个变量title:

    String title; //默认null

    我们页面build()代码如下:

    @override
      Widget build(BuildContext context) {
        return new WebviewScaffold(
          url: widget.url,
          appBar: _navigationBar(),
        );
      }
    
    PreferredSizeWidget _navigationBar () {
        return PreferredSize(
          child: new Stack(
            children: <Widget>[
              new CupertinoNavigationBar(
                middle: Text(this.title==null?"":this.title),
                border: Border.all(width: 0, color: CupertinoColors.darkBackgroundGray),
              ),
              new Positioned(
                child: _progressBar(),
                left: 0,
                bottom: 0,
                width: window.physicalSize.width/2,
                height: loading ? 2 : 0,
              )
            ],
          ),
          preferredSize: Size(window.physicalSize.width/2,44),
        );
      }
    
    

    我们关注中间设置标题的代码:

    middle: Text(this.title==null?"":this.title),

    这句话很好解释,当title为空的时候,标题显示空字符(即不显示),有内容就显示标题内容。当页面加载中的时候,title为空,所以显示成上图一的效果。那么如果在加载完成后,title获取了值后,让页面重新显示成图二呢?

    那么我们再页面加载完成,获取title的代码:

    //获取h5页面标题
      Future<String> getWebTitle() async {
        String script = 'window.document.title';
        var title = await
        flutterWebViewPlugin.evalJavascript(script);
        setState(() {
          this.title = title;
          print('####################   $title');
        });
        return title;
      }
    

    其中关键代码:

    setState(() {
    this.title = title;
    });

    这只title的代码放在了setState()里面,那么该代码执行完成后,会自动触发build()方法重新执行,重新执行到middle: Text(this.title==null?"":this.title),这时候title就已经有值了,页面标题就显示出来了。

    页面布局

    Flutter页面布局采用的是Flex布局原理,和ReactNative是一样的。如果你之前熟悉Flex布局,那么掌握Flutter页面布局将非常容易上手。

    以下面页面为例:

    页面布局截图

    上图展现的是一个商品列表的布局样式:整体:左右模式(Row); 右边里面:上下模式(Column)。

    下面贴出代码:

    new Row(
                    mainAxisAlignment: MainAxisAlignment.spaceAround,
                    children: [
                      new Container(
                        margin: new EdgeInsets.fromLTRB(0, 0, 12, 10),
                        color: new Color(0xfff5f5f5),
                        width: 100,
                        height: 100,
                        child: new Image(image: NetworkImage(item.imageUrl),fit: BoxFit.cover,)
                      ),
                      Expanded(
                        child: new Container(
                          height: 100,
                          child: rightColomnWidget(index)
                        ),
                      )
                    ],
                  ),
    
    //右边的列布局
      Widget rightColomnWidget(int index) {
        GoodsModel item = dataList[index];
        return new Column(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          new Text(
                            item.goodsName,
                            style: new TextStyle(
                              color: CupertinoColors.darkBackgroundGray,
                              fontSize: 18,
                              fontWeight: FontWeight.w600,
                            )
                          ),
                          new Text(
                            item.goodsDesc,
                            softWrap: true,
                            maxLines: 2,
                            overflow: TextOverflow.ellipsis,
                            style: new TextStyle(
                              color: CupertinoColors.darkBackgroundGray,
                              fontSize: 15,
                            )
                          ),
                          new Text(
                            item.goodsPrice + '元',
                            style: new TextStyle(
                              color: CupertinoColors.destructiveRed,
                              fontSize: 18,
                              fontWeight: FontWeight.w500,
                            )
                          )
                        ],
                      );
      }
    

    mainAxisAlignment: 主轴方向子元素如何排列。
    crossAxisAlignment:次轴方向子元素如何排列。

    Container布局里可以设置margin和padding。

    只有Row和Column的子元素可以是多个(children);
    其他的布局组件(Container, Center,Padding,Expanded)子元素只能一个:child.

    Expanded组件表示:父容器剩余的空间应该如何利用:

    flex: 0 自己尽量不扩展自己的大小。
    flex: 1 占满父容器剩余的空间。

    一般我们都是设置flex:1 (默认是1,可以不写)。

    列表组件:ListView

    ListView是Flutter内置的组件,相当于iOS 中的UITableview.

    ListView用法:

    new ListView.builder(
            itemCount: dataList.length,
            itemBuilder: (context, index){
              return new Center(
                child: new Container(
                  margin: new EdgeInsets.fromLTRB(12, 5, 12, 5),
                  child: new Column(
                    children: <Widget>[
                      rowWidget(index),
                      //分隔线
                      new Divider(height: 0.5,)
                    ],
                  )
                ),
              );
            },
    

    itemCount表示列表元素个数;
    itemBuilder:迭代的每一行里面的cell布局。

    这期暂时讲到这里,下期讲网络请求。

    相关文章

      网友评论

        本文标题:Flutter基本控件和页面布局

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