Flutter is really powerful, but the fly in the ointment is that the ecology needs to be improved. There is no universal basic UI library like Antd or Element in the front end.
The direct impact of this is that the development efficiency can not be improved, and it needs to spend a lot of time and energy on the packaging of basic components.
The official TabBar doesn't meet the demand and doesn't have a suitable wheel, so we have to make our own wheel. Next, I'll take you step by step to implement the custom TabBar
1, Objectives and effects
The demand objectives are:
- This page does not need the unified return key and Title on the left side of material
- There is a cancel button on the right. Click Cancel to return
- Click Tab to switch content with animation effect
- You can also toggle Tab by sliding the content area
The effect is as follows:
2, Realization idea
Divide the whole page into two parts, the Tab button above and the content area below.
In order to maintain generality, both the upper Tab and the lower content area need to be passed in by the caller. They are all Widget arrays
class STab extends StatefulWidget { // tab set final List<Widget> tabs; // Page collection final List<Widget> pages; STab({this.tabs, this.pages}); @override _STabState createState() => _STabState(); }
The overall layout of the page is a Column, with the Tab area above and the Content area below wrapped with Expand to achieve the effect of covering the whole screen.
The layout of the outer tab is always the top button of the Stack, and the layout of the tab cannot be cancelled. Multiple tab buttons are arranged with a horizontal layout Row, and the center alignment is set.
@override Widget build(BuildContext context) { return Container( child: Column( children: [ TabLayout(widget.tabs, selectedIndex, onTabChange, onCancelClick), ContentLayout(widget.pages, swipeControl, onPageChange) ], )); }
In the following content area, a third-party library is used to realize the effect of left-right sliding switching flutter_swiper . When you click tab, set the subscript of the swiper to switch the displayed content; When the swiper slides left and right, set the selected status of the tab to achieve the linkage between the selected status of the tab and the sliding of the swiper.
3, Component encapsulation
///tab switch component import 'package:flutter/material.dart'; import 'package:flutter_swiper/flutter_swiper.dart'; class STab extends StatefulWidget { // tab set final List<Widget> tabs; // Page collection final List<Widget> pages; STab({this.tabs, this.pages}); @override _STabState createState() => _STabState(); } class _STabState extends State<STab> { int selectedIndex = 0; SwiperController swipeControl = new SwiperController(); // tab index change callback void onTabChange(index) { setState(() { selectedIndex = index; }); swipeControl.move(index); } void onCancelClick() { print('cancel'); } void onPageChange(index) { setState(() { selectedIndex = index; }); } @override Widget build(BuildContext context) { return Container( child: Column( children: [ TabLayout(widget.tabs, selectedIndex, onTabChange, onCancelClick), ContentLayout(widget.pages, swipeControl, onPageChange) ], )); } } ///Tab layout above Widget TabLayout(tabs, selectedIndex, onTabChange, onRightButtonClick) { List<Widget> getItem() { List<Widget> children = []; for (var i = 0; i < tabs.length; i++) { children.add( GestureDetector( onTap: () { onTabChange(i); }, child: Container( padding: EdgeInsets.only(left: 20, right: 20, bottom: 10), decoration: BoxDecoration( border: Border( bottom: BorderSide( color: selectedIndex == i ? Color(0xff595959) : Colors.transparent, width: 3))), child: tabs[i], )), ); } return children; } return Stack( children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: getItem(), ), Positioned( top: 0, right: 0, child: GestureDetector( child: Container( height: 40, padding: EdgeInsets.only(left: 10, right: 10, bottom: 10), child: Text( 'cancel', textAlign: TextAlign.center, style: TextStyle(fontSize: 16), ), ), onTap: () { onRightButtonClick(); }, )) ], ); } ///The following page content layout Widget ContentLayout(pages, swipeControl, onIndexChanged) { return Expanded( child: Container( decoration: BoxDecoration(color: Colors.white), child: Swiper( itemCount: pages.length, itemBuilder: (BuildContext context, int index) { return pages[index]; }, loop: false, onIndexChanged: (index) { onIndexChanged(index); }, controller: swipeControl, )), ); }
4, How to use
Just pass in tabs and pages
class Demo extends StatelessWidget { final List<Widget> tabBodies = [ ExpensePage(), IncomePage(), ]; @override Widget build(BuildContext context) { return Scaffold( body: Container( padding: EdgeInsets.only(top: 30), decoration: BoxDecoration( color: Color(0xffF9DC62) ), child: STab( tabs: [ Text('expenditure', style: TextStyle(fontSize: 18, color: Colors.black),), Text('income', style: TextStyle(fontSize: 18, color: Colors.black)), ], pages: tabBodies, ), ), ); } }
5, Conclusion
The encapsulation of components is simply based on the business, without considering more situations. For example, the Cancel button on the right should also be imported from the outside, the color should also be imported from the outside, and whether the incoming data is verified to be legal... You can adjust the source code according to your actual business needs.
As for the degree of packaging, it is good to be suitable and sufficient.
If it's just for your own business, it's enough to encapsulate it. You only need to consider the scenarios in the business. If you want to open source and share it with others, you'd better give higher customization ability.