最近在做Flutter開發(fā)桌硫,碰到產品提的新的需求夭咬,類似于京東的地址選擇器,但是baidu了一堆铆隘,都只是定死層級的實現效果卓舵。不符合產品需求啊。自己碼一個膀钠,記錄一下掏湾。(可以根據需要,封裝成通用工具)
寫的不好肿嘲,請大佬輕噴融击。
效果圖
WeChat40dd05dfb8dccacffe42e73ee5ec7c22.png
需要注意,這里使用的是
TickerProviderStateMixin
而不是
SingleTickerProviderStateMixin
上代碼雳窟,注釋很清楚尊浪。
class EventAreaDialog extends StatefulWidget {
// 這個是我們網絡拉取的數據,根據實際你所需要的去變封救。應該是個樹形結構的model
SpaceData spaceData;
// 選中后的callback
EventAreaDialogCallBack eventAreaDialogCallBack;
EventAreaDialog({this.spaceData, this.eventAreaDialogCallBack});
@override
_EventAreaDialogState createState() => _EventAreaDialogState();
}
class _EventAreaDialogState extends State<EventAreaDialog>
with TickerProviderStateMixin {
//根據篩選出的數據
Map<int, List<Children>> datas = new Map();
//選中的model拇涤,根據業(yè)務需求獲取所需要的信息
Map<int, String> selectDatas = new Map();
//選中的名稱
Map<int, String> selectNameDatas = new Map();
//初始化的tab名稱
List<String> _tabs = ["請選擇"];
//tab的控制器
TabController _tabController;
//當前所選中的tab位置
int currentTabPos = 0;
@override
void initState() {
super.initState();
//初始化第一層級所需要顯示的內容
List<Children> list = [];
for (int i = 0; i < widget.spaceData.data.length; i++) {
Data data = widget.spaceData.data[i];
list.add(new Children(data.id, data.children, data.name));
}
//datas第一層級初始化賦值
datas[currentTabPos] = list;
_tabController = new TabController(length: _tabs.length, vsync: this);
}
@override
void dispose() {
super.dispose();
_tabController.dispose();
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: AnimatedPadding(
padding: MediaQuery.of(context).viewInsets, //邊距(必要)
duration: const Duration(milliseconds: 100), //時常 (必要)
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
color: Colors.white,
),
height: 350,
child: Flex(
direction: Axis.vertical,
children: <Widget>[
Container(
padding: new EdgeInsets.only(left: 10, right: 10),
height: 40,
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: TabBar(
controller: _tabController,
unselectedLabelColor: ColorUtils.TEXT_GRAY,
labelColor: ColorUtils.TEXT_BLUE,
indicatorColor: ColorUtils.TEXT_BLUE,
labelStyle: TextStyle(fontSize: 16),
isScrollable: true,
indicatorWeight: 3,
indicatorSize: TabBarIndicatorSize.label,
indicatorPadding: new EdgeInsets.fromLTRB(8, 0, 8, 0),
onTap: (index) {
if (!mounted) {
return;
}
setState(() {
//獲取當前tab有多少
int size = _tabs.length;
//記錄當前位置
currentTabPos = index;
//循環(huán)處理,將當前位置之后的數據去除
for (int i = size; i > currentTabPos + 1; i--) {
_tabs.removeAt(i - 1);
selectDatas.remove(i - 1);
selectNameDatas.remove(i - 1);
}
//這一步是重新創(chuàng)建tab
_tabController = new TabController(
length: _tabs.length, vsync: this);
//將當前tab移動到選中的位置上
_tabController.animateTo(currentTabPos);
});
},
tabs: _tabs.map((e) => Tab(text: e)).toList(),
),
),
),
Expanded(
flex: 1,
child: TabBarView(
children: _buildPages(),
controller: _tabController,
physics: NeverScrollableScrollPhysics(),
)),
],
),
),
),
);
}
/**
*創(chuàng)建tab界面
*/
List<Widget> _buildPages() {
List<Widget> pages = List();
for (int i = 0; i < _tabs.length; i++) {
Widget page = new ListView.builder(
padding: EdgeInsets.only(top: 15),
itemCount: datas[currentTabPos].length,
itemBuilder: (BuildContext context, int index) {
return _getListItem(index);
},
physics: new BouncingScrollPhysics(),
shrinkWrap: true,
);
pages.add(page);
}
return pages;
}
/**
*創(chuàng)建每個界面的item顯示
*/
Widget _getListItem(int index) {
Children children = datas[currentTabPos][index];
return GestureDetector(
child: Container(
alignment: Alignment.centerLeft,
color: Colors.white,
height: 50,
padding: new EdgeInsets.only(left: 15),
child: Text(children.name, style: TextStyle(fontSize: 15, color: selectDatas[currentTabPos] == children.id ? ColorUtils.TEXT_BLUE : ColorUtils.TEXT_GRAY),),
),
onTap: () {
if (!mounted) {
return;
}
setState(() {
if (currentTabPos > 0 && index == 0) {
Navigator.pop(context);
return;
}
if (children.children != null && children.children.length > 0) {//這是選中的tab還有下級數據時
_tabs[currentTabPos] = children.name;
currentTabPos++;
//加上一條【暫不選擇】的空數據
if(children.children[0].id != "-1") {
children.children.insert(0, new Children("-1", [], "暫不選擇"));
}
//修改datas的數據源
datas[currentTabPos] = children.children;
selectDatas[currentTabPos - 1] = children.id;
selectNameDatas[currentTabPos - 1] = children.name;
_tabs.add("請選擇");
_tabController =
new TabController(length: _tabs.length, vsync: this);
_tabController.animateTo(currentTabPos);
} else {//這是選中的tab沒有下級數據時
_tabs[currentTabPos] = children.name;
selectDatas[currentTabPos] = children.id;
selectNameDatas[currentTabPos] = children.name;
Navigator.pop(context);
}
//將選中的區(qū)域id添加到集合誉结,根據自己的業(yè)務去甄別
List<String> areaIds = [];
for(int i = 0; i < selectDatas.length; i++) {
areaIds.add(selectDatas[i]);
}
//這個是處理選中的名稱拼接鹅士,根據自己的業(yè)務去甄別
String names = "";
for(int i = 0; i < selectNameDatas.length; i++) {
if (i == selectNameDatas.length - 1) {
names += selectNameDatas[i];
} else {
names += "${selectNameDatas[i]}-";
}
}
//將選中的數據實時回調到需要的地方
widget.eventAreaDialogCallBack.onEventAreaDialogCallBack(names, areaIds);
});
},
);
}
}
最后使用方法
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (ctx) => EventAreaDialog(
spaceData: spaceData,
eventAreaDialogCallBack: this,
),backgroundColor: ColorUtils.CLEAR);