Flutter 组件:列表复用,支持 TabBar 不可点击状态

Posted 一叶飘舟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter 组件:列表复用,支持 TabBar 不可点击状态相关的知识,希望对你有一定的参考价值。

一、需求

工作中常遇到列表界面复用,每次敲重复代码有点繁琐,随顺手封装,方便复用;
值得注意的是flutter 中不可点击状态不是通过属性设置的,而是通过 IgnorePointer 组件实现的;

///TabBar+TabBarView
class TabBarTabBarView extends StatefulWidget 

/// TabBar + PageViewW
class TabBarPageView extends StatefulWidget 

二、screenshots

 

三、源码

TabBarTabBarView

//
//  TabBarPageView.dart
//  fluttertemplet
//
//  Created by shang on 10/22/21 5:03 PM.
//  Copyright © 10/22/21 shang. All rights reserved.
//

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:fluttertemplet/dartExpand/ddlog.dart';
import 'package:tuple/tuple.dart';


import 'package:flutter/material.dart';

///TabBar+TabBarView
class TabBarTabBarView extends StatefulWidget 

  final List<Tuple2<String, Widget>> items;

  final Color? indicatorColor;

  final Color? labelColor;

  final PageController? pageController;

  final TabController? tabController;

  final ValueChanged<int> onPageChanged;

  final bool Function(int)? canPageChanged;

  final bool isTabBarTop;

  TabBarTabBarView(
    Key? key,
    this.isTabBarTop = true,
    required this.items,
    this.indicatorColor,
    this.labelColor,
    this.pageController,
    this.tabController,
    required this.onPageChanged,
    this.canPageChanged,
  ) : super(key: key);


  @override
  _TabBarTabBarViewState createState() => _TabBarTabBarViewState();


class _TabBarTabBarViewState extends State<TabBarTabBarView> with TickerProviderStateMixin 

  late TabController _tabController;
  late TabController _tabController1 = TabController(length: widget.items.length, vsync: this);

  ///是否允许滚动
  bool get canScrollable 
    if (widget.canPageChanged != null && widget.canPageChanged!(_tabController.index) == false) 
      return false;
    
    return true;
  

  @override
  void initState() 
    _tabController = widget.tabController ?? TabController(length: widget.items.length, vsync: this)
    ..addListener(() 
      // ddlog(_tabController.index);
      setState(() 
        _tabController1.animateTo( _tabController.index);
      );
      widget.onPageChanged(_tabController.index);
    );

    super.initState();
  

  @override
  void dispose() 
    _tabController.dispose();

    super.dispose();
  

  @override
  Widget build(BuildContext context) 
    final list = [
      _buildTabBar(),
      _buildTabBarView(),
    ];
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: widget.isTabBarTop ? list : list.reversed.toList(),
    );
  

  Widget _buildTabBar() 
    final textColor = widget.labelColor ?? Theme
        .of(context)
        .colorScheme
        .secondary;
    final bgColor = widget.indicatorColor ?? Colors.white;

    // final bgColor = Theme.of(context).colorScheme.secondary;
    // final textColor = Colors.white;

    BoxDecoration indicatorTop = BoxDecoration(
      border: Border(
        top: BorderSide(
          // color: textColor ,
            color: textColor,
            width: 3.0
        ),
      ),
    );

    BoxDecoration indicatorBom = BoxDecoration(
      border: Border(
        bottom: BorderSide(
          // color: textColor ,
            color: textColor,
            width: 3.0
        ),
      ),
    );


    final tabBar = TabBar(
      controller: _tabController1,
      tabs: widget.items.map((e) => Tab(text: e.item1)).toList(),
      labelColor: textColor,
      indicator: widget.isTabBarTop ? indicatorBom : indicatorTop,
      onTap: (index) 
        // ddlog([index, _tabController.index]);
        setState(() 
          _tabController.animateTo( _tabController1.index);
        );
        // widget.onPageChanged(_tabController.index);
      ,
    );

    if (!canScrollable) 
      return IgnorePointer(
        child: tabBar,
      );
    

    if (widget.isTabBarTop) 
      return tabBar;
    

    return Material(
        color: bgColor,
        child: SafeArea(
          child: tabBar,
        )
    );
  

  Widget _buildTabBarView() 
    return Expanded(
      child: TabBarView(
        controller: _tabController,
        physics: canScrollable ? BouncingScrollPhysics() : NeverScrollableScrollPhysics(),
        children: widget.items.map((e) => e.item2).toList(),
      ),);
  


TabBarPageView

//
//  TabBarPageView.dart
//  fluttertemplet
//
//  Created by shang on 10/22/21 5:03 PM.
//  Copyright © 10/22/21 shang. All rights reserved.
//

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:fluttertemplet/dartExpand/ddlog.dart';
import 'package:tuple/tuple.dart';

/// TabBar + PageViewW
class TabBarPageView extends StatefulWidget 

  final List<Tuple2<String, Widget>> items;

  final Color? indicatorColor;

  final Color? labelColor;

  final PageController? pageController;

  final TabController? tabController;

  final ValueChanged<int> onPageChanged;

  final bool Function(int)? canPageChanged;

  final bool isTabBarTop;

  TabBarPageView(
    Key? key,
    this.isTabBarTop = true,
    required this.items,
    this.indicatorColor,
    this.labelColor,
    this.pageController,
    this.tabController,
    required this.onPageChanged,
    this.canPageChanged,
  ) : super(key: key);

  @override
  _TabBarPageViewState createState() => _TabBarPageViewState();


class _TabBarPageViewState extends State<TabBarPageView> with SingleTickerProviderStateMixin 

  late TabController _tabController;
  late PageController _pageController;

  ///是否允许滚动
  bool get canScrollable 
    if (widget.canPageChanged != null && widget.canPageChanged!(_tabController.index) == false) 
      return false;
    
    return true;
  

  @override
  void initState() 
    _tabController = widget.tabController ?? TabController(length: widget.items.length, vsync: this);
    _pageController = widget.pageController ?? PageController(initialPage: 0, keepPage: true);
    // ..addListener(() 
    //   ddlog(_pageController.page);
    //
    // );

    super.initState();
  

  @override
  void dispose() 
    _tabController.dispose();
    _pageController.dispose();

    super.dispose();
  

  @override
  Widget build(BuildContext context) 
    final list = [
      _buildTabBar(),
      _buildPageView(),
    ];
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: widget.isTabBarTop ? list : list.reversed.toList(),
    );
  

  Widget _buildTabBar() 
    final textColor = widget.labelColor ?? Theme
        .of(context)
        .colorScheme
        .secondary;
    final bgColor = widget.indicatorColor ?? Colors.white;

    // final bgColor = Theme.of(context).colorScheme.secondary;
    // final textColor = Colors.white;

    BoxDecoration indicatorTop = BoxDecoration(
      border: Border(
        top: BorderSide(
          // color: textColor ,
            color: textColor,
            width: 3.0
        ),
      ),
    );

    BoxDecoration indicatorBom = BoxDecoration(
      border: Border(
        bottom: BorderSide(
          // color: textColor ,
            color: textColor,
            width: 3.0
        ),
      ),
    );


    final tabBar = TabBar(
      controller: _tabController,
      tabs: widget.items.map((e) => Tab(text: e.item1)).toList(),
      labelColor: textColor,
      indicator: widget.isTabBarTop ? indicatorBom : indicatorTop,
      onTap: (index) 
        // ddlog(index);
        setState(() 
          _pageController.jumpToPage(index);
        );
      ,
    );

    if (!canScrollable) 
      return IgnorePointer(
        child: tabBar,
      );
    

    if (widget.isTabBarTop) 
      return tabBar;
    

    return Material(
        color: bgColor,
        child: SafeArea(
          child: tabBar,
        )
    );
  

  Widget _buildPageView() 
    return Expanded(
      child: PageView(
        controller: _pageController,
        physics: canScrollable ? BouncingScrollPhysics() : NeverScrollableScrollPhysics(),
        children: widget.items.map((e) => e.item2).toList(),
        onPageChanged: (index) 
          widget.onPageChanged(index);
          setState(() 
            _tabController.animateTo(index);
          );
        ,
      ));
  

源代码:githubhttps://github.com/shang1219178163/flutter_templet_project/blob/main/lib/pages/tabBar_reuse_page_demo.dart

以上是关于Flutter 组件:列表复用,支持 TabBar 不可点击状态的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 专题75 图解基本 TabBar 标签导航栏 #yyds干货盘点#

有没有办法在 Flutter 的 TabBar 中创建下拉列表?

Flutter 仿京东商品详情页嵌套滚动组件

Flutter学习-阶段案例

六Flutter自定义Tabbar

ListView 基础列表组件水平 列表组件图标组件