Flutter基本控件和页面布局

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 createState() {
    return _CommonWebPageState();
  }
  
}

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

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

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

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

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

页面状态变化更新机制

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

setState()

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

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

Flutter基本控件和页面布局_第1张图片
加载中
Flutter基本控件和页面布局_第2张图片
加载完成

我们首先需要定义一个变量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: [
          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 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页面布局将非常容易上手。

以下面页面为例:

Flutter基本控件和页面布局_第3张图片
页面布局截图

上图展现的是一个商品列表的布局样式:整体:左右模式(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: [
                  rowWidget(index),
                  //分隔线
                  new Divider(height: 0.5,)
                ],
              )
            ),
          );
        },

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

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

你可能感兴趣的:(Flutter基本控件和页面布局)