Flutter 滚动布局1

164 阅读2分钟

ListView

可滚动组件都有一个 controller 属性的 ScrollController,通过它可以进行一些滚动设置, 比如 jumpTo(double offset)animateTo(double offset,...)

ScrollController _controller = ScrollController(

  initialScrollOffset: 1000, //初始滚动到的位置
  keepScrollOffset: true,    //是否记录滚动位置 
);

//如果需要在路由跳转时记录滚动位置,不仅要设置keepScrollOffset,同时还要设置
//key: PageStorageKey(1), 数字用于区分页面中多个滚动视图时使用

1. 默认构造

ListView(
  shrinkWrap: true
  padding: const EdgeInsets.all(10),
  children: 
       [
        Text("第一段文字"),
        Text("第二段文字"),
        Text("第三段文字"),
        Text("第四段文字"),
        Text("第五段文字"),
        Text("第六段文字"),
      ],
);

2. builder构造

ListView.builder(

  key: PageStorageKey(1), //保存滚动位置
  controller: controller,
  itemExtent: 100,
  itemCount: 50,
  padding: EdgeInsets.all(10),
  
  itemBuilder: (BuildContext context, int index) {
  
    return Container(
        color: index % 2 == 0 ? Colors.red : Colors.green,
        child: ListTile(title: Text("a----$index")),
    );
  },
)

3. separated构造

ListView.separated(

  itemCount: 10,
  padding: EdgeInsets.all(10),
  
  separatorBuilder: (BuildContext context, int index) {
  
    return Text("这是第$index行文字");
  },

  itemBuilder: (BuildContext context, int index) {
     return const DecoratedBox(
        decoration: BoxDecoration(color: Colors.green),
        child: SizedBox(height: 10),
     );
  },
)

GridView

普通构造

GridView主要通过gridDelegate 进行布局,SliverGridDelegate共有两个子类可以直接使用:

1. SliverGridDelegateWithFixedCrossAxisCount

固定交叉轴条目数量

GridView(

      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          mainAxisSpacing: 30,     //主轴方向的间距
          crossAxisCount: 3,       //横轴三个子widget
          crossAxisSpacing: 20,    //交叉轴方向的间距
         childAspectRatio: 1.0,   //子widget宽高比为1
    ),
    children: <Widget>[
    
      Icon(Icons.ac_unit),
      Icon(Icons.airport_shuttle),
      Icon(Icons.all_inclusive),
      Icon(Icons.beach_access),
      Icon(Icons.cake),
      Icon(Icons.free_breakfast)
    ],
)

类似于 count 构造方法

GridView.count(

  crossAxisCount: 150,

  children: <Widget>[

    Icon(Icons.ac_unit),
    Icon(Icons.airport_shuttle),
    Icon(Icons.all_inclusive),
    Icon(Icons.beach_access),
    Icon(Icons.cake),
  ],
)

2. SliverGridDelegateWithMaxCrossAxisExtent

指定条目在交叉轴方向的最大尺寸

GridView(
    gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(

      maxCrossAxisExtent: 100.0,  //子元素在横轴上的最大长度
      childAspectRatio: 2.0,      //宽高比为2
    ),

    children: <Widget>[
    
      Icon(Icons.ac_unit),
      Icon(Icons.airport_shuttle),
      Icon(Icons.all_inclusive),
      Icon(Icons.beach_access),
      Icon(Icons.cake),
      Icon(Icons.free_breakfast)
    ],
)

类似于 extent构造方法

GridView.extent(

  maxCrossAxisExtent: 150,

  children: <Widget>[

    Icon(Icons.ac_unit),
    Icon(Icons.airport_shuttle),
    Icon(Icons.all_inclusive),
    Icon(Icons.beach_access),
    Icon(Icons.cake),
  ],
)

以上两种方式都强制约束条目组件尺寸,所以单纯的GridView 无法实现瀑布流效果。 另外这种方式不适合创建条目数量较大的列表。

builder/custom 构造

GridView.builder(

  gridDelegate:  SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 30 ),
        
  itemCount: 2,
  
  itemBuilder: (BuildContext context, int index) {
  
     return Icon(Icons.ac_unit);
  },

)

PageView

@override
Widget build(BuildContext context) {
  
  var children = <Widget>[];
 
  // 生成 6 个单独页面
  for (int i = 0; i < 6; ++i) {
    children.add( Page( text: '$i'));
  }

  return PageView(
    // scrollDirection: Axis.vertical, // 滑动方向为垂直方向
    //controller:PageController(),    //控制器
    pageSnapping:true,                //是否整页滑动
    children: children,
  );
}

TabBarView

一般是顶部TabBar,下面TabBarView,通过同一个控制器进行联动

AppBar.bottom通常是TabBar等,通过PreferredSize可设置为任意组件

Scaffold(
  appBar: AppBar(
    backgroundColor: ColorRes.public_00A99A,
    centerTitle: true, //居中展示 !!!
    title: const Text('资讯'),
    bottom: _customTab(state.tabs),//TabBar 一般在底部
  ),
  
  //TabBarView一般做页面body
  body: TabBarView(     
    controller: _tabController,
    children: _itemPage(state.tabs),
  ),
),

生成TabBar

TabBar(

  //是否滚动
  isScrollable: true,
  //对数组的遍历处理
  tabs: tab.map<Tab>((e) => Tab(text: e.name)).toList(),
  //和TabBarView控制器
  controller: _tabController,

  // 选中指示条颜色
  indicatorColor: ColorRes.public_5083AA,
 
  //选中或未选中时文字颜色及
  labelColor: ColorRes.public_000000,
  unselectedLabelColor: ColorRes.public_999999,

  // 未选择尺寸
  indicatorSize: TabBarIndicatorSize.label,

  //style里面设置颜色无效
  labelStyle: const TextStyle(

  fontSize: DimenRes.public_18, fontWeight: FontWeight.bold),

  unselectedLabelStyle: const TextStyle(

  fontSize: DimenRes.public_16, fontWeight: FontWeight.bold),

),

注意 由于TabBarView 与 TabBar 需要联动,所以必须初始化 下,TabController, 同时还要混入TickerProviderStateMixin,同时还需要注意释放的问题,

_tabController = TabController(
    length: state.tabs.length,
    vsync: this, //固定写法
);

所以实赋中直接用·DefaultTabController`做为父组件,就可以避免创建控制器和混入方法的问题。

DefaultTabController(

  length: state.tabs.length,
  child: Scaffold(
    appBar: AppBar(
      backgroundColor: ColorRes.public_00A99A,
      centerTitle: true, //居中展示 !!!
      title: const Text('资讯'),
      bottom: _customTab(state.tabs),
    ),
    body: TabBarView(
      children: _itemPage(state.tabs),
    ),
  ),
 );