第六章:点点滴滴,在于积累

开发的过程就是最好的学习过程,也是检验自己能力过程,更是升华自己的过程。

明事理的壮汉——Expanded

在我们在开发Flutter页面的过程中,Column和Row无疑是布局中用的最多的控件,但有的时候会发现个别布局太大会超出屏幕,调试时受影响的边缘会出现黄色和黑色条纹图案。入下图:

解决办法:在Text外面套上一个Expanded控件。最终的效果,如下图:

为什么会出现这种情况呢?搜索了半天也没找到答案。我猜想:是像Row和Column这样的控件只会给子控件一个方向和对齐方式,没有在大小上约束,子控件按父的方向上依次绘制自己,所以才会超出屏幕。这个时候,Expanded控件就派上了用场。他好比我们在安卓中的控件的weight属性,这个控件在默认约束的范围内尽可能的扩大自己。比如上面的例子里,Expanded会占据屏幕中除了Image控件宽度外的空间,给Text在宽度上加上限制。除此之外,多个Expanded控件在同一个Row或者Column的时候,Expanded通过属性flex设置比重值来决定本身的大小,用法和android中的weight使用方法相同。

PageView中子页面切换重复刷新问题

我们在用PageView的时候,发现页面切换后,子页面会重新初始化。这种交互给用户的体验很不好。如何避免页面切换页面刷新呢。子页面必须是StatefulWidget,在State中添加AutomaticKeepAliveClientMixin。具体使用请参考:https://blog.csdn.net/xcf111/article/details/95318987。这篇帖子,很详细。

TabBar+PageView联动

在开发中用到TabBar+PageView这个组合的时候非常多,两个控件的联动效果很重要。在联动过程中这两个控件的滚动事件的相互作用,在TabBar跨顺序选中时,会出现不正常的效果。在这里提前给大家指明一下。下面给出一种解决办法:

import 'package:flutter/material.dart';

class MyHomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => HomeState();
}

class TabTitle {
  String title;
  int id;

  TabTitle(this.title, this.id);
}

class HomeState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  TabController mTabController;
  PageController mPageController = PageController(initialPage: 0);
  List<TabTitle> tabList;
  var currentPage = 0;
  var isPageCanChanged = true;

  @override
  void initState() {
    super.initState();
    initTabData();
    mTabController = TabController(
      length: tabList.length,
      vsync: this,
    );
    mTabController.addListener(() {
      //TabBar的监听
      if (mTabController.indexIsChanging) {
        //判断TabBar是否切换
        print(mTabController.index);
        onPageChange(mTabController.index, p: mPageController);
      }
    });
  }

  initTabData() {
    tabList = [
      new TabTitle('推荐', 10),
      new TabTitle('热点', 0),
      new TabTitle('社会', 1),
      new TabTitle('娱乐', 2),
      new TabTitle('体育', 3)
    ];
  }

  //联动的的关键方法
  onPageChange(int index, {PageController p, TabController t}) async {
    //判断是哪一个切换,
    if (p != null) {//TabBar导致的滚动
      //TabBar切换导致的,屏蔽PageView的滚动事件
      isPageCanChanged = false;
      await mPageController.animateToPage(index,
          duration: Duration(milliseconds: 500),
          curve: Curves.ease); //等待pageview切换完毕,再释放pageivew监听
      isPageCanChanged = true;
    } else {//PageView导致的滚动
      mTabController.animateTo(index); //切换Tabbar
    }
  }

  @override
  void dispose() {
    super.dispose();
    mTabController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("首页"),
        backgroundColor: Color(0xffd43d3d),
      ),
      body: Column(
        children: <Widget>[
          Container(
            color: new Color(0xfff4f5f6),
            height: 38.0,
            child: TabBar(
              isScrollable: true,//是否可以滚动 
              controller: mTabController,
              labelColor: Colors.red,
              unselectedLabelColor: Color(0xff666666),
              labelStyle: TextStyle(fontSize: 16.0),
              tabs: tabList.map((item) {
                return Tab(
                  text: item.title,
                );
              }).toList(),
            ),
          ),
          Expanded(
            child: PageView.builder(
              itemCount: tabList.length,
              onPageChanged: (index) {
                if (isPageCanChanged) {
                  //由于pageview切换是会回调这个方法,又会触发切换tabbar的操作,所以定义一个flag,控制pageview的回调
                  onPageChange(index);
                }
              },
              controller: mPageController,
              itemBuilder: (BuildContext context, int index) {
                return Text(tabList[index].title);
              },
            ),
          )
        ],
      ),
    );
  }
}

为方便大家观看,特此贴出整理后的代码。逻辑很简单,请参考注释。

以上代码抽取自帖子:https://www.jianshu.com/p/7f5b7e7d3c9a

Dart语言小亮点——方法函数中的可选参数

在上个问题中我们在看提供的代码时,会发现onPageChage这个关键方法的参数有点蹊跷。我们在其他的开发语言中没接触过这种写法。通过了解发现这是Dart语言中函数方法还有可选参数这么一说。熟悉以后会发现这种写法,也挺人性化。具体学习请参考下文:

可选参数可以是位置参数,也可以是命名参数,但不能同时是位置参数和命名参数。

1.可选命名参数

调用函数时,可以使用paramName : value指定命名参数。例如:

enableFlags(bold: true, hidden: false);

定义函数时,使用{ param 1,param 2,... }指定命名参数:

/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold, bool hidden}) {...}

Flutter实例创建表达式可能会变得复杂,因此Widget构造函数专门使用命名参数。这使得实例创建表达式更容易阅读。

你可以用@required注释任何Dart代码中的命名参数(不仅仅是Flutter),以指示它是必需的参数。例如:

const Scrollbar({Key key, @required Widget child})

Scrollbar被构建时,分析器会在缺少child参数时报错。

Require被定义在meta包里。要么直接导入package:meta/meta.dart,要么导入另一个导出meta包,如Flutter的package:flutter/material.dart.

2.可选位置参数

[]中包装一组函数参数,将它们标记为可选位置参数:

String say(String from, String msg, [String device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}

这里有一个调用这个函数的例子,没有可选参数:

assert(say('Bob', 'Howdy') == 'Bob says Howdy');

下面是用第三个参数调用这个函数的例子:

assert(say('Bob', 'Howdy', 'smoke signal') ==
    'Bob says Howdy with a smoke signal');

3.默认参数值

函数可以使用=为命名参数和位置参数定义默认值。默认值必须是编译时常量。如果未提供默认值,则默认值为null。

下面是为命名参数设置默认值的示例:

///设置bold默认值为true,实质hidden'默认值为false
void enableFlags({bool bold = false, bool hidden = false}) {...}

// bold为true; hidden为 false.
enableFlags(bold: true);

下一个示例显示如何设置位置参数的默认值:

String say(String from, String msg,
    [String device = 'carrier pigeon', String mood]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  if (mood != null) {
    result = '$result (in a $mood mood)';
  }
  return result;
}

assert(say('Bob', 'Howdy') ==
    'Bob says Howdy with a carrier pigeon');

你也可以将集合(lists)映射(maps)作为默认值传递。下面的示例定义了一个函数doStuff(),该函数指定了一个默认的集合来放置list参数和默认的映射来放置gitfts的参数

void doStuff(
    {List<int> list = const [1, 2, 3],
    Map<String, String> gifts = const {
      'first': 'paper',
      'second': 'cotton',
      'third': 'leather'
    }}) {
  print('list:  $list');
  print('gifts: $gifts');
}

本篇文章到这里结束了,敬请期待下篇文章吧。^_^

 

悄悄话:

安卓开发者的福利:小绿人  一个实用的安卓开发工具箱,搜集了数千个开源项目。拿走不谢^_^。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值