123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707 |
- // ignore_for_file: unnecessary_set_literal
- import 'package:flutter/material.dart';
- import 'package:wisdom_cli/src/assets.gen.dart';
- import 'package:wisdom_cli/wisdom_cli.dart';
- ///Picker抽象类
- ///[WPicker],
- ///[WPickerUtil.infinity],
- ///[WPickerUtil.multiple],
- ///[WPickerUtil.single]中的[getData]返回的[List.item]类,应该要实现该抽象类
- ///
- abstract class WPickerEntity {
- ///显示的名字
- String get selectName;
- ///唯一值
- Object get selectUnique;
- ///是否禁用
- bool get selectDisabled;
- }
- ///
- ///通用快捷的构造[WPickerEntity],
- ///只需要传入一个[name],
- ///[unique]为次要的。不传也可以
- class SinglePickerEntity implements WPickerEntity {
- final String? name;
- final Object? unique;
- final bool? disabled;
- final List? child;
- final List? cascade;
- SinglePickerEntity({
- this.name,
- this.unique,
- this.disabled = false,
- this.child,
- this.cascade,
- });
- @override
- String get selectName => name ?? '';
- @override
- Object get selectUnique => unique ?? '';
- @override
- bool get selectDisabled => disabled ?? false;
- List? get childList => child ?? [];
- List get cascadeList => cascade ?? [];
- ///Map快速构造[List<SinglePickerEntity>]
- ///
- ///```json
- ///{
- ///unique1:name1,
- ///unique2:name2,
- ///unique3:name3,
- ///unique4:name4,
- ///unique5:name5,
- ///unique6:name6,
- ///....
- ///}
- ///```
- static Future<List<SinglePickerEntity>> map2entity(
- Map<dynamic, String> data) async {
- final keys = data.keys;
- final list = keys
- .map((key) => SinglePickerEntity(name: data[key]!, unique: key))
- .toList();
- return list;
- }
- ///Map快速构造[List<SinglePickerEntity>]
- ///
- ///此构造方法[unique]也会使用[name],请确保数组无重复项
- ///```json
- ///[name1,name2,name3,...]
- ///```
- static Future<List<SinglePickerEntity>> list2entity(List<String> data) async {
- final list = data.map((e) => SinglePickerEntity(name: e)).toList();
- return list;
- }
- ///Map快速构造[List<SinglePickerEntity>]
- /// list<dynamic> to entity
- /// @data 列表数据
- /// @label 标题字段
- /// @value 值字段
- static Future<List<SinglePickerEntity>> listMap2entity(
- List<dynamic> data, label, value,
- {List? enableValue}) async {
- final list = data
- .map(
- (e) => SinglePickerEntity(
- name: e['$label'],
- unique: e['$value'],
- disabled: (enableValue != null && enableValue.length > 0)
- ? enableValue.contains(e['$value'])
- : false,
- ),
- )
- .toList();
- return list;
- }
- }
- ///选择组件、非常基础。
- ///
- ///[getData]异步返回一个[List],
- ///列表项[List.item]应该是一个[WPickerEntity]的实现类
- ///
- ///具体使用可参[底部弹出层封装]
- ///[WPickerUtil.single]、
- ///[WPickerUtil.multiple]、
- ///[WPickerUtil.infinity]
- ///
- ///
- class WPicker<T extends WPickerEntity> extends StatefulWidget {
- WPicker({
- Key? key,
- this.getData,
- this.multiple = false,
- this.dense = false,
- this.initialValue = const [],
- this.listData,
- // this.enableValue = const [],
- this.onChanged,
- this.isSearch = false,
- }) : assert(multiple != null),
- assert(dense != null),
- assert(initialValue != null),
- assert(initialValue!.length <= 1 || multiple == true),
- super(key: key);
- ///是否多选
- final bool? multiple;
- final bool? dense;
- /// 是否可搜索
- final bool? isSearch;
- ///列表项[List.item]应该是一个[WPickerEntity]的实现类
- final WSinglePickerGetData<T>? getData;
- final List<T>? listData;
- ///为了单选多选的通用性,[initialValue]是一个List。
- ///当单选时,即[multiple==false],应满足[initialValue.length<=1]
- final List<T>? initialValue;
- // final List enableValue;
- ///选项发生改变时触发。
- ///[isTap==true]时,说明触发该函数的是由用户点击引起的,
- ///[isTap==false]时,说明触发该函数可能是由初始化,或其他组件内部原因引起的。
- final void Function(List<T>? value, bool isTap)? onChanged;
- @override
- _WPickerState<T> createState() => _WPickerState<T>();
- }
- class _WPickerState<T extends WPickerEntity> extends State<WPicker<T>> {
- bool loading = false;
- List<T>? list = [];
- List<T>? initList = [];
- List<T>? value;
- void getData() async {
- if (widget.getData != null) {
- loading = true;
- try {
- initList = (await widget.getData!());
- list = initList;
- } finally {
- loading = false;
- }
- setState(() {});
- } else {
- loading = true;
- try {
- initList = widget.listData;
- list = initList;
- } finally {
- loading = false;
- }
- setState(() {});
- }
- }
- @override
- void initState() {
- super.initState();
- value = List.of(widget.initialValue ?? []);
- widget.onChanged!(value, false);
- getData();
- }
- ///是否已经选中?
- ///判断唯一值
- bool isSelected(T v) {
- return value!.indexWhere((e) => e.selectUnique == v.selectUnique) != -1;
- }
- ///点击事件
- ///会触发widget.onChanged(selects, true);
- void ontap(T v) {
- if (!(v.selectDisabled)) {
- if (!widget.multiple!) {
- value = [v];
- } else {
- final exit = isSelected(v);
- if (exit) {
- value!.removeWhere((e) => e.selectUnique == v.selectUnique);
- } else {
- value!.add(v);
- }
- }
- setState(() {});
- if (widget.onChanged != null) {
- widget.onChanged!(value, true);
- }
- }
- }
- void onInput(v) {
- List cList =
- (initList!.map((e) => e.selectName.contains(v) ? e : null)).toList();
- List<T> tempList = [];
- cList.forEach((element) {
- if (element != null) {
- tempList.add(element);
- }
- });
- list = tempList;
- setState(() {});
- }
- /// 为解决flutter截断不完整
- String breakWord(String text) {
- if (text.isEmpty) {
- return text;
- }
- String breakWord = ' ';
- text.runes.forEach((element) {
- breakWord += String.fromCharCode(element);
- breakWord += '\u200B';
- });
- return breakWord;
- }
- @override
- Widget build(BuildContext context) {
- if (loading) return Center(child: WLoading());
- final colorScheme = WTheme.of(context).colorScheme;
- return Stack(
- children: [
- if (list == null || list!.length == 0 || list?.isEmpty == true) ...[
- WEmptyWidget(
- margin: EdgeInsets.only(
- top: widget.isSearch == true ? 59.pt : 15.pt,
- ),
- text: WisText('暂无数据'),
- ),
- ] else ...[
- ListView(
- padding: EdgeInsets.only(
- top: widget.isSearch == true ? 49.pt : 0,
- ),
- children: list!.map(
- (value) {
- return Wisdom(
- width: MediaQuery.of(context).size.width,
- margin: EdgeInsets.symmetric(horizontal: 15.pt),
- border: Border(bottom: Divider.createBorderSide(context)),
- child: ListTile(
- onTap: () => ontap(value),
- dense: widget.dense,
- contentPadding: EdgeInsets.zero,
- selected: isSelected(value),
- enabled: !(value.selectDisabled),
- title: Wisdom.row(
- width: MediaQuery.of(context).size.width,
- // mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- // 增加勾选按钮样式
- if (isSelected(value)) ...[
- Wisdom(
- padding: EdgeInsets.only(right: 10.pt),
- child: widget.multiple == true
- ? Image(
- image: AssetList.$fxxz_png_image,
- height: 16.pt)
- : Image(
- image: AssetList.$danxuan_png_image,
- height: 16.pt),
- ),
- WisText(
- '${breakWord(value.selectName)}',
- color: colorScheme.primary,
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- ).asExpanded(),
- ] else ...[
- Wisdom(
- padding: EdgeInsets.only(right: 10.pt),
- child: widget.multiple == true
- ? Image(
- image: AssetList.$dx_png_image,
- height: 16.pt)
- : Image(
- image: AssetList.$danxuanyuan_png_image,
- height: 16.pt),
- ),
- WisText(
- '${breakWord(value.selectName)}',
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- ).asExpanded(),
- ],
-
- ],
- ),
- ),
- );
- },
- ).toList(),
- ),
- ],
- if (widget.isSearch == true) ...[
- Positioned(
- top: 0,
- left: 0,
- right: 0,
- child: WSearch(
- hintText: '输入关键词搜索',
- onSubmitted: (v) => onInput(v),
- onCancel: () => onInput(''),
- onChanged: (v) => onInput(v),
- moonlight: false,
- padding: EdgeInsets.symmetric(vertical: 10.pt),
- isDark: true,
- backColor: Colors.white,
- ),
- ),
- ],
- ],
- );
- }
- }
- class WTreePicker<T extends WPickerEntity> extends StatefulWidget {
- WTreePicker({
- Key? key,
- required this.data,
- required this.labelname,
- required this.valuename,
- required this.childname,
- required this.firstName,
- required this.firstValue,
- this.multiple = false,
- this.dense = false,
- this.initialValue = const [],
- // this.enableValue = const [],
- this.onChanged,
- this.isSearch = false,
- }) : super(key: key);
- final String? labelname;
- final String? valuename;
- final String? childname;
- final String? firstName;
- final String? firstValue;
- ///是否多选
- final bool? multiple;
- final bool? dense;
- /// 是否可搜索
- final bool? isSearch;
- ///列表项[List.item]应该是一个[WPickerEntity]的实现类
- final List? data;
- ///为了单选多选的通用性,[initialValue]是一个List。
- ///当单选时,即[multiple==false],应满足[initialValue.length<=1]
- final List? initialValue;
- // final List enableValue;
- ///选项发生改变时触发。
- ///[isTap==true]时,说明触发该函数的是由用户点击引起的,
- ///[isTap==false]时,说明触发该函数可能是由初始化,或其他组件内部原因引起的。
- final void Function(List value, bool isTap)? onChanged;
- @override
- _WTreePickerState<T> createState() => _WTreePickerState<T>();
- }
- class _WTreePickerState<T extends WPickerEntity> extends State<WTreePicker<T>> {
- bool? loading = false;
- List? list = [];
- var _fistValue;
- List? initList = [];
- List? secondList = [];
- String? _searchValue;
- List? value;
- void getData() async {
- if (widget.data != null) {
- loading = true;
- try {
- initList = widget.data;
- list = initList!;
- _fistValue = initList!.length > 0 ? initList![0] : null;
- secondList = _fistValue[widget.childname] ?? [];
- } finally {
- loading = false;
- }
- setState(() {});
- }
- }
- @override
- void initState() {
- super.initState();
- value = List.of(widget.initialValue ?? []);
- getData();
- }
- ///是否已经选中?
- ///判断唯一值
- bool isSelected(v) {
- return value!
- .indexWhere((e) => e[widget.valuename] == v[widget.valuename]) !=
- -1;
- }
- ///点击事件
- ///会触发widget.onChanged(selects, true);
- void ontap(v) {
- // if (!(v?.selectDisabled ?? false)) {
- if (widget.multiple == false) {
- value = [v];
- } else {
- final exit = isSelected(v);
- if (exit) {
- value!.removeWhere((e) => e[widget.valuename] == v[widget.valuename]);
- } else {
- value!.add(v);
- }
- }
- setState(() {});
- if (widget.onChanged != null) {
- widget.onChanged!(value!, true);
- }
- }
- onSelectFirst(v) {
- _fistValue = v;
- secondList = _fistValue[widget.childname] ?? [];
- setState(() {});
- }
- void onInput(v) {
- _searchValue = v;
- if (v == null || v == '') {
- list = initList!;
- } else {
- List tempList = [];
- initList!.forEach((ele) => {
- ele[widget.childname].forEach((item) => {
- if (item[widget.labelname].contains(v))
- {
- tempList.add(item),
- }
- }),
- });
- list = tempList;
- }
- setState(() {});
- }
- @override
- Widget build(BuildContext context) {
- final colorScheme = WTheme.of(context).colorScheme;
- if (loading == true) return Center(child: WLoading());
- return Stack(
- children: [
- Wisdom.row(
- color: Colors.white,
- mainAxisSize: MainAxisSize.min,
- children: [
- if (_searchValue != null && _searchValue != '') ...[
- if (list == null || list!.length == 0) ...[
- Wisdom(
- width: MediaQuery.of(context).size.width,
- padding: EdgeInsets.only(
- top: widget.isSearch == true ? 49.pt : 0,
- ),
- child: Center(
- child: WEmptyWidget(
- text: WisText('搜索不到,空空如也~'),
- ),
- ),
- ),
- ] else ...[
- SizedBox(
- height: double.infinity,
- width: MediaQuery.of(context).size.width,
- child: ListView(
- padding: EdgeInsets.only(
- top: widget.isSearch == true ? 49.pt : 0,
- ),
- children: list!.map(
- (value) {
- return Wisdom(
- margin: EdgeInsets.symmetric(horizontal: 15.pt),
- border:
- Border(bottom: Divider.createBorderSide(context)),
- child: ListTile(
- onTap: () => ontap(value),
- dense: widget.dense,
- contentPadding: EdgeInsets.zero,
- selected: false,
- enabled: true,
- title: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- WisText(
- '${value[widget.labelname] ?? '--'}',
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- ).asExpanded(),
- // 增加勾选按钮样式
- if (isSelected(value)) ...[
- Wisdom(
- padding: EdgeInsets.only(left: 10.pt),
- child: Image(
- image: AssetList.$xzh_png_image,
- height: 10.pt,
- ),
- ),
- ],
- ],
- ),
- ),
- );
- },
- ).toList(),
- ),
- ).asExpanded(),
- ]
- ] else ...[
- if (list != null && list!.length > 0) ...[
- SizedBox(
- height: double.infinity,
- width: 130.pt,
- child: ListView(
- padding: EdgeInsets.only(
- top: widget.isSearch == true ? 49.pt : 0,
- ),
- children: list!.map(
- (value) {
- return Wisdom(
- margin: EdgeInsets.symmetric(horizontal: 15.pt),
- border:
- Border(bottom: Divider.createBorderSide(context)),
- child: ListTile(
- onTap: () => onSelectFirst(value),
- dense: widget.dense,
- contentPadding: EdgeInsets.zero,
- selected: _fistValue != null &&
- _fistValue[widget.firstValue] ==
- value[widget.firstValue],
- enabled: true,
- title: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- if (_fistValue != null &&
- _fistValue[widget.firstValue] ==
- value[widget.firstValue]) ...[
- Wisdom(
- color: colorScheme.primary,
- height: 14.pt,
- width: 2.pt,
- margin: EdgeInsets.only(right: 5.pt),
- ),
- ],
- WisText(
- '${value[widget.firstName] ?? '--'}',
- maxLines: 1,
- color: _fistValue != null &&
- _fistValue[widget.firstValue] ==
- value[widget.firstValue]
- ? colorScheme.primary
- : null,
- overflow: TextOverflow.ellipsis,
- ).asExpanded(),
- ],
- ),
- ),
- );
- },
- ).toList(),
- ),
- ),
- if (secondList != null && secondList!.length > 0) ...[
- SizedBox(
- height: double.infinity,
- width: MediaQuery.of(context).size.width - 130.pt,
- child: ListView(
- padding: EdgeInsets.only(
- top: widget.isSearch == true ? 49.pt : 0,
- ),
- children: secondList!.map(
- (value) {
- return Wisdom(
- margin: EdgeInsets.only(right: 15.pt),
- border: Border(
- bottom: Divider.createBorderSide(context)),
- child: ListTile(
- onTap: () => ontap(value),
- dense: widget.dense,
- contentPadding: EdgeInsets.zero,
- selected: false,
- enabled: true,
- title: Row(
- mainAxisAlignment:
- MainAxisAlignment.spaceBetween,
- children: [
- WisText(
- '${value[widget.labelname] ?? '--'}',
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- ).asExpanded(),
- // 增加勾选按钮样式
- if (isSelected(value)) ...[
- Wisdom(
- padding: EdgeInsets.only(left: 10.pt),
- child: Image(
- image: AssetList.$xzh_png_image,
- height: 10.pt,
- ),
- ),
- ],
- ],
- ),
- ),
- );
- },
- ).toList(),
- ),
- )
- ] else ...[
- Wisdom(
- width: MediaQuery.of(context).size.width - 130.pt,
- padding: EdgeInsets.only(
- top: widget.isSearch == true ? 49.pt : 0,
- ),
- child: Center(
- child: WEmptyWidget(
- text: WisText('暂无数据'),
- ),
- ),
- ),
- ]
- ] else ...[
- Wisdom(
- width: MediaQuery.of(context).size.width,
- padding: EdgeInsets.only(
- top: widget.isSearch == true ? 49.pt : 0,
- ),
- child: Center(
- child: WEmptyWidget(
- text: WisText('暂无数据'),
- ),
- ),
- ),
- ]
- ]
- ],
- ),
- if (widget.isSearch == true) ...[
- Positioned(
- top: 0,
- left: 0,
- right: 0,
- child: WSearch(
- onCancel: () => onInput(null),
- hintText: '输入关键词搜索',
- onSubmitted: (v) => onInput(v),
- onChanged: (v) => onInput(v),
- moonlight: false,
- padding: EdgeInsets.symmetric(vertical: 10.pt),
- isDark: true,
- backColor: Colors.white,
- ),
- ),
- ],
- ],
- );
- }
- }
|