ModalBarrier、AnimatedModalBarrier、DefaultAssetBundle、DropdownButtonFormField、FormField、InheritedWidget、PaginatedDataTable

This commit is contained in:
toly
2020-12-20 16:58:17 +08:00
parent ccc9414285
commit da9764a51c
14 changed files with 581 additions and 45 deletions

Binary file not shown.

View File

@@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:ui' as ui;
/// create by 张风捷特烈 on 2020-04-01
/// contact me by email 1981462002@qq.com
/// 说明: 320 DefaultAssetBundle 默认资源包
/// 一个 InheritedWidget设置 AssetBundle 对象后,该节点后的节点上下文可以通过 DefaultAssetBundle.of(context) 获取 AssetBundle 对象用于访问资源文件。
// {
// "widgetId": 320,
// "name": 'DefaultAssetBundle 介绍',
// "priority": 1,
// "subtitle":
// "【bundle】 : *资源包 【AssetBundle】\n"
// "【child】 : *子组件 【Widget】\n"
// "我们可以定义自己的 DefaultAssetBundle 来供后续节点使用,也可以直接使用默认的。该案例演示通过框架提供的 DefaultAssetBundle 加载一张资源图片进行显示。",
// }
class DefaultAssetBundleDemo extends StatefulWidget {
@override
_DefaultAssetBundleDemoState createState() => _DefaultAssetBundleDemoState();
}
class _DefaultAssetBundleDemoState extends State<DefaultAssetBundleDemo> {
ui.Image _image;
@override
void initState() {
super.initState();
_load();
}
@override
Widget build(BuildContext context) {
return Container(
width: 150,
height: 150,
color: Colors.blue.withOpacity(0.1),
padding: EdgeInsets.all(10),
margin: EdgeInsets.all(10),
child: _image==null?Container():RawImage(image: _image,fit: BoxFit.cover,),
);
}
void _load() async{
AssetBundle info = DefaultAssetBundle.of(context);
ByteData data = await info.load('assets/images/sabar.webp');
_image = await decodeImageFromList(data.buffer.asUint8List());
setState(() {
});
}
}

View File

@@ -36,24 +36,24 @@ class TestBody extends StatelessWidget {
margin: EdgeInsets.only(left: 40,right: 40),
alignment: Alignment.center,
color: Theme.of(context).primaryColor.withOpacity(0.1),
child: Text('点我进入下页')));
child: Text('InheritedTheme')));
}
void _toNextPage(BuildContext context) {
final NavigatorState navigator = Navigator.of(context);
final CapturedThemes themes =
InheritedTheme.capture(from: context, to: navigator.context);
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext _) {
return themes.wrap(Container(
alignment: Alignment.center,
color: Colors.white,
child: Text('Flutter Unit'),
));
},
),
);
// final NavigatorState navigator = Navigator.of(context);
// final CapturedThemes themes =
// InheritedTheme.capture(from: context, to: navigator.context);
//
// Navigator.of(context).push(
// MaterialPageRoute(
// builder: (BuildContext _) {
// return themes.wrap(Container(
// alignment: Alignment.center,
// color: Colors.white,
// child: Text('Flutter Unit'),
// ));
// },
// ),
// );
}
}

View File

@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/9/21
/// contact me by email 1981462002@qq.com
/// 说明: 346 InheritedWidget 传承组件
/// 该类是抽象类作用是可以在本上下文存储数据在其后续节点的上下文中共享该数据。有很多实现类包括各种主题组件、MediaQuery等。
/// link: 167,319,328,324,331
///
// {
// "widgetId": 346,
// "name": 'InheritedWidget 使用',
// "priority": 1,
// "subtitle":
// "【child】 : 子组件 【Widget】\n"
// "下面是一个简单的自定义 InheritedWidget实现信息的子树共享。",
// }
class InheritedWidgetDemo extends StatelessWidget {
final String info =
'InheritedWidget 是一个抽象类,不可以直接使用。可以自定义对应共享数据的子类,如这里的通过 InfoInheritedWidget 实现:当前这段话可以在任意子树节点上下文获取。'
'一般都会定义一个 XXX.of(context) 的方法来获取数据,如 MediaQuery.ofTheme.of 等。';
@override
Widget build(BuildContext context) {
return InfoInheritedWidget(
info: info,
child: InfoWidget(),
);
}
}
class InfoWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
String info = InfoInheritedWidget.of(context).info;
return Container(
color: Colors.blue.withOpacity(0.1),
padding: EdgeInsets.all(10),
margin: EdgeInsets.all(10),
child: Text(info),
);
}
}
class InfoInheritedWidget extends InheritedWidget {
final String info;
InfoInheritedWidget({Key key, this.info, @required Widget child})
: super(key: key, child: child);
@override
bool updateShouldNotify(covariant InfoInheritedWidget oldWidget) =>
info != oldWidget.info;
static InfoInheritedWidget of(BuildContext context) =>
context.dependOnInheritedWidgetOfExactType<InfoInheritedWidget>();
}

View File

@@ -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),)
]),
);
}
}

View File

@@ -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();
}

View 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),
);
}
}

View File

@@ -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();
}
}

View File

@@ -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),
],
);
}

View File

@@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020-04-01
/// contact me by email 1981462002@qq.com
/// 说明: 212 ModalBarrier 屏障模
/// 相当于一块幕布,防止用户与其背后的 Widget 交互,可以通过 dismissible 决定点击时,是否触发返回栈。源码中用于 Dialog 相关组件。
/// link: 227,126,127,128
// {
// "widgetId": 212,
// "name": 'ModalBarrier 介绍',
// "priority": 1,
// "subtitle":
// "【dismissible】 : 点击是否返回 【bool】\n"
// "【color】 : 颜色 【Color】",
// }
class ModalBarrierDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 200,
height: 100,
child: Stack(alignment: Alignment.center, children: [
ModalBarrier(
dismissible: true,
color: Colors.grey.withOpacity(0.3),
),
Text('点击背景返回')
]),
);
}
}

View File

@@ -30,3 +30,5 @@ export '../ProxyWidget/TableCell/node1_base.dart';
export '../ProxyWidget/KeepAlive/node1_base.dart';
export '../ProxyWidget/CupertinoUserInterfaceLevel/node1_base.dart';
export '../ProxyWidget/InheritedTheme/node1_base.dart';
export '../ProxyWidget/DefaultAssetBundle/node1_base.dart';
export '../ProxyWidget/InheritedWidget/node1_base.dart';

View File

@@ -78,8 +78,11 @@ export '../StatefulWidget/RawGestureDetector/node1_base.dart';
export '../StatefulWidget/Dismissible/node1_base.dart';
export '../StatefulWidget/AutomaticKeepAlive/node1_base.dart';
export '../StatefulWidget/AnimatedModalBarrier/node1_base.dart';
export '../StatefulWidget/FormField/node1_base.dart';
export '../StatefulWidget/AnimatedBuilder/node1_base.dart';
export '../StatefulWidget/TweenAnimationBuilder/node1_base.dart';
export '../StatefulWidget/PaginatedDataTable/node1_base.dart';
export '../StatefulWidget/RawKeyboardListener/node1_base.dart';
export '../StatefulWidget/Dismissible/node2_direction.dart';
@@ -100,6 +103,7 @@ export '../StatefulWidget/AnimatedPositioned/node1_base.dart';
export '../StatefulWidget/AnimatedPositionedDirectional/node1_base.dart';
export '../StatefulWidget/ExpansionPanelList/node1_base.dart';
export '../StatefulWidget/DropdownButtonFormField/node1_base.dart';
export '../StatefulWidget/Ink/node1_base.dart';
export '../StatefulWidget/Ink/node2_image.dart';

View File

@@ -50,6 +50,7 @@ export '../StatelessWidget/Divider/node1_base.dart';
export '../StatelessWidget/Divider/node2_height.dart';
export '../StatelessWidget/ScrollView/node1_base.dart';
export '../StatelessWidget/ModalBarrier/node1_base.dart';
export '../StatelessWidget/BoxScrollView/node1_base.dart';
export '../StatelessWidget/FloatingActionButton/node1_base.dart';
export '../StatelessWidget/FloatingActionButton/node2_mini.dart';

View File

@@ -83,6 +83,34 @@ class WidgetsMap {
return [
InheritedThemeDemo(),
];
case "ModalBarrier":
return [
ModalBarrierDemo(),
];
case "AnimatedModalBarrier":
return [
AnimatedModalBarrierDemo(),
];
case "DefaultAssetBundle":
return [
DefaultAssetBundleDemo(),
];
case "DropdownButtonFormField":
return [
DropdownButtonFormFieldDemo(),
];
case "FormField":
return [
FormFieldDemo(),
];
case "PaginatedDataTable":
return [
PaginatedDataTableDemo(),
];
case "InheritedWidget":
return [
InheritedWidgetDemo(),
];
case "ScrollView":
return [
ScrollViewDemo(),