forked from lxm_flutter/FlutterUnit
✨ ModalBarrier、AnimatedModalBarrier、DefaultAssetBundle、DropdownButtonFormField、FormField、InheritedWidget、PaginatedDataTable
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// create by 张风捷特烈 on 2020-04-01
|
||||
/// contact me by email 1981462002@qq.com
|
||||
/// 说明: 227 AnimatedModalBarrier 动画屏障模
|
||||
/// 内部依赖 ModalBarrier 实现,功能一致,只不过该组件可以传入一个颜色动画,进行过渡展现。
|
||||
/// link: 212
|
||||
// {
|
||||
// "widgetId": 227,
|
||||
// "name": 'AnimatedModalBarrier 介绍',
|
||||
// "priority": 1,
|
||||
// "subtitle":
|
||||
// "【dismissible】 : 点击是否返回 【bool】\n"
|
||||
// "【color】 : 颜色 【Animation<Color>】",
|
||||
// }
|
||||
class AnimatedModalBarrierDemo extends StatefulWidget {
|
||||
@override
|
||||
_AnimatedModalBarrierDemoState createState() => _AnimatedModalBarrierDemoState();
|
||||
}
|
||||
|
||||
class _AnimatedModalBarrierDemoState extends State<AnimatedModalBarrierDemo>
|
||||
with SingleTickerProviderStateMixin {
|
||||
AnimationController _controller;
|
||||
Animation<Color> _color;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller =
|
||||
AnimationController(vsync: this, duration: Duration(seconds: 2))..forward();
|
||||
_color = ColorTween(begin: Colors.blue, end: Colors.purple)
|
||||
.animate(_controller);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: 200,
|
||||
height: 100,
|
||||
child: Stack(alignment: Alignment.center, children: [
|
||||
AnimatedModalBarrier(
|
||||
dismissible: true,
|
||||
color: _color,
|
||||
),
|
||||
Text('点击背景返回',style: TextStyle(color: Colors.white),)
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// create by 张风捷特烈 on 2020/9/21
|
||||
/// contact me by email 1981462002@qq.com
|
||||
/// 说明: 223 DropdownButtonFormField 表单下拉框
|
||||
/// 底层依赖 DropdownButton 实现,所以基本属性类似。但拥有 FormField 的特性,可以回调 onSaved、validator 方法。
|
||||
/// link: 55,222
|
||||
///
|
||||
// {
|
||||
// "widgetId": 223,
|
||||
// "name": '表单下拉框简单使用',
|
||||
// "priority": 1,
|
||||
// "subtitle":
|
||||
// "【items】 : 子组件列表 【List<DropdownMenuItem<T>>】\n"
|
||||
// "【validator】 : 表单验证回调 【FormFieldValidator<T>】\n"
|
||||
// "【onSaved】 : 表单保存回调 【FormFieldSetter<T>】\n"
|
||||
// "其他属性详见 DropdownButton,表单校验特性详见 FormField。",
|
||||
// }
|
||||
|
||||
class DropdownButtonFormFieldDemo extends StatefulWidget {
|
||||
@override
|
||||
_DropdownButtonFormFieldDemoState createState() => _DropdownButtonFormFieldDemoState();
|
||||
}
|
||||
|
||||
class _DropdownButtonFormFieldDemoState extends State<DropdownButtonFormFieldDemo> {
|
||||
Color _color;
|
||||
final _colors = [Colors.red, Colors.yellow, Colors.blue, Colors.green];
|
||||
final _info = ["红色", "黄色", "蓝色", "绿色"];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Wrap(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 20),
|
||||
width: 50,
|
||||
height: 50,
|
||||
color: _color??_colors[0],
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
width: 80,
|
||||
child: DropdownButtonFormField<Color>(
|
||||
value: _color,
|
||||
elevation: 1,
|
||||
hint: Text('选择颜色',style: TextStyle(fontSize: 12),),
|
||||
icon: Icon(
|
||||
Icons.expand_more,
|
||||
size: 20,
|
||||
color: _color,
|
||||
),
|
||||
items: _buildItems(),
|
||||
onChanged: (v) => setState(() => _color = v)
|
||||
),
|
||||
)
|
||||
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
List<DropdownMenuItem<Color>> _buildItems() => _colors
|
||||
.map((e) => DropdownMenuItem<Color>(
|
||||
value: e,
|
||||
child: Text(
|
||||
_info[_colors.indexOf(e)],
|
||||
style: TextStyle(color: e),
|
||||
)))
|
||||
.toList();
|
||||
}
|
||||
36
lib/views/widgets/StatefulWidget/FormField/node1_base.dart
Normal file
36
lib/views/widgets/StatefulWidget/FormField/node1_base.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// create by 张风捷特烈 on 2020-04-01
|
||||
/// contact me by email 1981462002@qq.com
|
||||
/// 说明: 222 FormField 表单字段
|
||||
/// 一个表单字段,需要在 Form 组件中使用,内含泛型 T 的字段作为状态量,对根据字段的更新和验证会触发相应回调。
|
||||
/// link:198,199,223,
|
||||
// {
|
||||
// "widgetId": 222,
|
||||
// "name": 'FormField 介绍',
|
||||
// "priority": 1,
|
||||
// "subtitle":
|
||||
// "【builder】 : 内容构造器 【FormFieldBuilder<T>】\n"
|
||||
// "【initialValue】 : 初始值 【T】\n"
|
||||
// "【validator】 : 验证函数 【FormFieldValidator<String> 】\n"
|
||||
// "【enabled】 : 是否有效 【bool】\n"
|
||||
// "【onSaved】 : 表单save时回调 【FormFieldSetter<String>】",
|
||||
// }
|
||||
class FormFieldDemo extends StatelessWidget {
|
||||
final String info =
|
||||
'FormField 代表表单中的一个字段,对于字符串类型的字段,框架中封装了 TextFormField 以便使用;下拉选择的字段,用 DropdownButtonFormField。'
|
||||
'目前框架中 FormField 的子类也只有这两个。既然是表单字段,必然是要和 Form 组件一起使用。通过对 Form 添加 GlobalKey ,来获取 FormState 对象。'
|
||||
'当 FormState 调用 save 方法时,所有的 FormField 都会触发 onSave 方法,当 FormState 调用 validate 方法时,所有的 FormField 都会触发 validate 方法。';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
|
||||
return Container(
|
||||
color: Colors.blue.withOpacity(0.1),
|
||||
padding: EdgeInsets.all(10),
|
||||
margin: EdgeInsets.all(10),
|
||||
child: Text(info),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// create by 张风捷特烈 on 2020-04-01
|
||||
/// contact me by email 1981462002@qq.com
|
||||
/// 说明: 235 PaginatedDataTable 可分页表格
|
||||
/// 一个功能丰富的可分页表格组件,可指定分页数、排列、页码前后切换。
|
||||
/// link: 110,102
|
||||
// {
|
||||
// "widgetId": 235 ,
|
||||
// "name": 'PaginatedDataTable 使用',
|
||||
// "priority": 1,
|
||||
// "subtitle":
|
||||
// "【header】 : 表名 【Widget】\n"
|
||||
// "【rowsPerPage】 : 每页记录数 【int】\n"
|
||||
// "【actions】 : 操作组件 【List<Widget>】\n"
|
||||
// "【columns】 : 数据列 【List<DataColumn>】\n"
|
||||
// "【sortColumnIndex】 : 排序列索引 【int】\n"
|
||||
// "【sortAscending】 : 是否升序 【bool】\n"
|
||||
// "【onSelectAll】 : 全选回调 【ValueSetter<bool>】\n"
|
||||
// "【onRowsPerPageChanged】 : 分页改变监听 【ValueChanged<int>】\n"
|
||||
// "【availableRowsPerPage】 : 可用分页列表 【List<int>】\n"
|
||||
// "【source】 : 数据源 【DataTableSource】",
|
||||
// }
|
||||
class PaginatedDataTableDemo extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() => _PaginatedDataTableDemoState();
|
||||
}
|
||||
|
||||
class _PaginatedDataTableDemoState extends State<PaginatedDataTableDemo> {
|
||||
int _rowsPerPage = 5;
|
||||
|
||||
int _sortColumnIndex;
|
||||
bool _sortAscending = true;
|
||||
|
||||
final DessertDataSource _dessertsDataSource = DessertDataSource();
|
||||
|
||||
void sort<T>(
|
||||
Comparable<T> getField(HeroInfo d), int columnIndex, bool ascending) {
|
||||
_dessertsDataSource.sort<T>(getField, ascending);
|
||||
setState(() {
|
||||
_sortColumnIndex = columnIndex;
|
||||
_sortAscending = ascending;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 300,
|
||||
width: 350,
|
||||
child: SingleChildScrollView(
|
||||
child: PaginatedDataTable(
|
||||
actions: <Widget>[
|
||||
IconButton(icon: Icon(Icons.add), onPressed: null),
|
||||
],
|
||||
header: const Text(
|
||||
'《旷古奇书》-角色预设',
|
||||
style: TextStyle(color: Colors.blue),
|
||||
),
|
||||
rowsPerPage: _rowsPerPage,
|
||||
availableRowsPerPage: [5, 8, 10, 15],
|
||||
onRowsPerPageChanged: (int value) {
|
||||
setState(() {
|
||||
_rowsPerPage = value;
|
||||
});
|
||||
},
|
||||
sortColumnIndex: _sortColumnIndex,
|
||||
sortAscending: _sortAscending,
|
||||
onSelectAll: _dessertsDataSource._selectAll,
|
||||
columns: <DataColumn>[
|
||||
DataColumn(
|
||||
label: const Text('角色名称'),
|
||||
onSort: (int columnIndex, bool ascending) => sort<String>(
|
||||
(HeroInfo d) => d.name, columnIndex, ascending)),
|
||||
DataColumn(
|
||||
label: const Text('主场卷部'),
|
||||
tooltip: '人物主要出场的作品.',
|
||||
numeric: true,
|
||||
onSort: (int columnIndex, bool ascending) => sort<String>(
|
||||
(HeroInfo d) => d.calories, columnIndex, ascending)),
|
||||
DataColumn(
|
||||
label: const Text('种族'),
|
||||
numeric: true,
|
||||
onSort: (int columnIndex, bool ascending) => sort<String>(
|
||||
(HeroInfo d) => d.fat, columnIndex, ascending)),
|
||||
DataColumn(
|
||||
label: const Text('性别'),
|
||||
numeric: true,
|
||||
onSort: (int columnIndex, bool ascending) => sort<String>(
|
||||
(HeroInfo d) => d.carbs, columnIndex, ascending)),
|
||||
],
|
||||
source: _dessertsDataSource),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class HeroInfo {
|
||||
HeroInfo(this.name, this.calories, this.fat, this.carbs);
|
||||
|
||||
final String name;
|
||||
final String calories;
|
||||
final String fat;
|
||||
final String carbs;
|
||||
bool selected = false;
|
||||
}
|
||||
|
||||
class DessertDataSource extends DataTableSource {
|
||||
final List<HeroInfo> _desserts = <HeroInfo>[
|
||||
HeroInfo('捷特', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('龙少', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('巫缨', '《幻将录》', "人族", "女"),
|
||||
HeroInfo('林兮', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('九方玄玉', '《风神传》', "神族", "男"),
|
||||
HeroInfo('七日洪荒', '《风神传》', "魔族", "男"),
|
||||
HeroInfo('林昔瑶', '《封妖志》', "鬼族", "女"),
|
||||
HeroInfo('林兮鬼帝', '《封妖志》', "鬼族", "男"),
|
||||
HeroInfo('艾隆', '《封妖志》', "鬼族", "男"),
|
||||
HeroInfo('语熙华', '《风神传》', "道族", "男"),
|
||||
HeroInfo('雪玉宛如', '《幻将录》', "人族", "女"),
|
||||
HeroInfo('破千', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('浪封', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('虎翼穷奇', '《封妖志》', "妖族", "男"),
|
||||
HeroInfo('凯', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('荆棘', '《幻将录》', "人族", "女"),
|
||||
HeroInfo('龙右', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('梦千', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('梦小梦', '《幻将录》', "人族", "女"),
|
||||
HeroInfo('梦瞳', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('十戈', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('计画天', '《幻将录》', "人族", "女"),
|
||||
HeroInfo('士方', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('巫妻孋', '《幻将录》', "人族", "女"),
|
||||
HeroInfo('木时黎', '《永恒传说》', "人族", "男"),
|
||||
HeroInfo('木艾奇', '《永恒传说》', "人族", "男"),
|
||||
HeroInfo('张风', '《永恒传说》', "人族", "男"),
|
||||
HeroInfo('薛剑儿', '《永恒传说》', "人族", "男"),
|
||||
HeroInfo('李月', '《永恒传说》', "人族", "女"),
|
||||
HeroInfo('刘雪', '《永恒传说》', "人族", "女"),
|
||||
HeroInfo('葛心', '《永恒传说》', "人族", "女"),
|
||||
HeroInfo('步映容', '《幻将录》', "人族", "女"),
|
||||
HeroInfo('莫慈良', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('莫向阳', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('莫子薇', '《永恒传说》', "人族", "女"),
|
||||
HeroInfo('藏凯阳', '《永恒传说》', "人族", "男"),
|
||||
HeroInfo('奇雨歆', '《永恒传说》', "人族", "女"),
|
||||
HeroInfo('林天蕊', '《永恒传说》', "人族", "女"),
|
||||
HeroInfo('吴灏然', '《永恒传说》', "人族", "男"),
|
||||
HeroInfo('何解连', '《永恒传说》', "人族", "男"),
|
||||
HeroInfo('步络尘', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('拓雷', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('炽阳骑', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('正构', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('烈', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('梦华君', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('初星', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('梦飞烟', '《幻将录》', "人族", "男"),
|
||||
HeroInfo('武落英', '《幻将录》', "人族", "女"),
|
||||
HeroInfo('古千缘', '《幻将录》', "人族", "男"),
|
||||
];
|
||||
|
||||
void sort<T>(Comparable<T> getField(HeroInfo d), bool ascending) {
|
||||
_desserts.sort((HeroInfo a, HeroInfo b) {
|
||||
if (!ascending) {
|
||||
final HeroInfo c = a;
|
||||
a = b;
|
||||
b = c;
|
||||
}
|
||||
final Comparable<T> aValue = getField(a);
|
||||
final Comparable<T> bValue = getField(b);
|
||||
return Comparable.compare(aValue, bValue);
|
||||
});
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
int _selectedCount = 0;
|
||||
|
||||
@override
|
||||
DataRow getRow(int index) {
|
||||
if (index >= _desserts.length) return null;
|
||||
final HeroInfo dessert = _desserts[index];
|
||||
return DataRow.byIndex(
|
||||
index: index,
|
||||
selected: dessert.selected,
|
||||
onSelectChanged: (bool value) {
|
||||
if (dessert.selected != value) {
|
||||
_selectedCount += value ? 1 : -1;
|
||||
assert(_selectedCount >= 0);
|
||||
dessert.selected = value;
|
||||
notifyListeners();
|
||||
}
|
||||
},
|
||||
cells: <DataCell>[
|
||||
DataCell(Center(child: Text('${dessert.name}'))),
|
||||
DataCell(Center(child: Text('${dessert.calories}'))),
|
||||
DataCell(Center(child: Text('${dessert.fat}'))),
|
||||
DataCell(Center(child: Text('${dessert.carbs}'))),
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isRowCountApproximate => false;
|
||||
|
||||
@override
|
||||
int get rowCount => _desserts.length;
|
||||
|
||||
@override
|
||||
int get selectedRowCount => _selectedCount;
|
||||
|
||||
void _selectAll(bool checked) {
|
||||
for (HeroInfo dessert in _desserts) dessert.selected = checked;
|
||||
_selectedCount = checked ? _desserts.length : 0;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@@ -23,37 +23,23 @@ class _CustomTextFormFieldState extends State<CustomTextFormField> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child:
|
||||
Stack(
|
||||
alignment: Alignment.centerRight,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 350,
|
||||
child: UnconstrainedBox(
|
||||
child: Container(
|
||||
width: 200,
|
||||
height: 70,
|
||||
child: TextFormField(
|
||||
style: TextStyle(textBaseline: TextBaseline.alphabetic),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: 'username',
|
||||
),
|
||||
validator: _validateUsername,
|
||||
onFieldSubmitted: _onFieldSubmitted,
|
||||
onSaved: _onSaved,
|
||||
),
|
||||
),
|
||||
),
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
SizedBox(width: 40),
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(textBaseline: TextBaseline.alphabetic),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: 'username',
|
||||
),
|
||||
Positioned(
|
||||
top: 0, right: 0, child: _buildSubmitButton(context)),
|
||||
],
|
||||
validator: _validateUsername,
|
||||
onFieldSubmitted: _onFieldSubmitted,
|
||||
onSaved: _onSaved,
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildSubmitButton(context),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user