前语
flutter
开发过程中,除了加载框之类的,dialog
系列也会头疼,包括alert弹窗提示
、picker
、date-picker
等也需求自行挑选或定制,很麻烦,且不一定能运用到自己想要的作用
这里自己更偏好ios风格
,因而运用 Cupertino
风格对应组件封装成了咱们直接能够用的弹窗、picker
系列组件,且默许的作用也不是很好,因而自行改动了一下
本来是想运用 adaptive_dialog 弹窗,但感觉两个渠道两种风格的差点意思,个人更偏好 ios风格,所以乎才想起来独自做了 ios 风格的弹窗
然后参阅了 flutter-widget ,发现 picker 用起来也不是那么给力,就也从头定制了一下,顺道增加了比较常用风格的 picker
、mutli-picker
、date-picker
以便于运用
事例demo(alert、action-sheet、input-alert、picker、mutli-picker、date-picker)
如果想发布到 pub.dev
能够参阅这里
本章也增加了怎么避免运用 dialog
时传递 context
的办法
ps
:除此之外还增加了 ios 风格的 menu 事例,仅作为参阅,未封装成组件
ps2
:如果该库和其他的姓名有抵触,能够运用模块化方法导入,liru: import 'dialogs/dialogs.dart' as dialog;
alert
ios
风格的 alert
,作用如下所示
运用如下所示,回来的成果在 Future 中,为bool类型,毕竟撤销位置也或许不是撤销而是有其他事情
showCupertinoAlert(
// context: context,
title: "提示",
message: "检测到未登录,请前往登陆!",
confirmText: "立即前往",
cancelText: "撤销",
// isDestructiveCancel: true,
).then((res) {
print(res);
setState(() {
value = res.toString();
});
});
总共提供了这些特点,能够根据状况传递运用
BuildContext? context, //如果没设置大局,需求传递自己的context
String title = '',
String message = '',
confirmText = '确认',
cancelText = '撤销',
isShowCancel = true,
isDestructiveConfirm = false,
isDestructiveCancel = false,
input-alert
ios
风格的 input-alert
,作用如下所示
运用如下所示,回来 Future
为输入的 text
,撤销则不触发回调
showCupertinoInputAlert(
// context: context,
title: "标题",
message: '内容',
isDestructiveCancel: true,
onChanged: (String text) {
print(text);
setState(() {
value = text;
});
},
).then((res) {
print(res);
//能够干别的事情
if (res != null) {
setState(() {
value = res;
});
}
});
详细参数如下所示,经过姓名就知道功用了,不多介绍
BuildContext? context, //如果没设置大局,需求传递自己的context
String title = '',
String message = '',
confirmText = '确认',
cancelText = '撤销',
isShowCancel = true,
isDestructiveConfirm = false,
isDestructiveCancel = false,
String? placeholder,
String? defaultText,
TextAlign? textAlign,
OverlayVisibilityMode? clearButtonMode,
TextInputType? keyboardType,
String? Function(String value)? onChanged, //内容改动后的回调,能够经过回来值来校验text输入等操作
action-sheet
ios
风格的 action-sheet
,作用如下所示
运用如下所示,回来 Future
为挑选的 index
,内容能够根据 外部的actions
获取实践挑选内容,撤销不触发回调
能够经过 isDestructive、isLoading
可选参数,来为独自的按钮置红
或者显现加载中
,如果有未加载结束的能够运用 isLoading
参数来显现加载中的状况
final actions = <ActionSheetItem>[
ActionSheetItem(text: "确认"),
ActionSheetItem(text: "特殊", isDestructive: true),
ActionSheetItem(text: "加载中...", isLoading: true),
];
showCupertinoActionSheet(
// context: context,
title: '演示',
message: "请挑选一个作用",
actions: actions, //运用了updater之后,该参数能够疏忽
// isDestructiveCancel: true,
).then((res) {
print(res);
setState(() {
value = actions[res].text!;
});
});
如果用到了网络数,需求用到加载中,能够运用
final ActionSheetUpdater updater = ActionSheetUpdater();
getActionSheetData() {
//有网络数据时请求主张运用该参数
//给定默许作用
updater.actions = <ActionSheetItem>[
// ActionSheetItem(text: "确认"),
// ActionSheetItem(text: "特殊", isDestructive: true),
ActionSheetItem(text: "加载中...", isLoading: true),
];
Future.delayed(const Duration(seconds: 5), () {
setState(() {
value = '加载完了';
});
//如果要用到前面的actions,能够从updater中获取
// final actions = updater.actions;
//也能够先赋值action后调用更新,也能够像下面似的直接传参更新
updater.update(<ActionSheetItem>[
ActionSheetItem(text: "确认"),
ActionSheetItem(text: "特殊", isDestructive: true),
ActionSheetItem(text: "加载中...", isLoading: true),
]);
});
}
void showActionSheet() {
//假设这是网络数据
showCupertinoActionSheet(
// context: context,
title: '演示',
message: "请挑选一个作用",
// actions: actions,//运用了updater之后,该参数能够疏忽
updater: updater,
).then((res) {
print(res);
setState(() {
value = actions[res].text!;
});
});
}
参数如下所示
BuildContext? context, //如果没设置大局,需求传递自己的context
String? title,
String? message,
ActionSheetUpdater? updater, //外部声明该变量用与实时更新action
List<ActionSheetItem>? actions,
isShowCancel = true,
cancelText = '撤销',
isDefaultActionCancel = false, //粗体
isDestructiveCancel = false, //红色
picker
单列picker
,作用如下所示
运用如下所示,需求传入一维数组,默许索引不传递则为0,成果回来索引index
和挑选的item
showCupertinoPicker(
// context: context,
pickerList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
defaultIndex: 2,
onValueChanged: (index, item) {
//picker滑动的回调
print(index);
print(item);
},
).then((res) {
setState(() {
value = res.item.toString();
});
print(res.index);
print(res.item);
});
参数如下所示
BuildContext? context, //如果没设置大局,需求传递自己的context
required List pickerList,
defaultIndex = 0,
String? title = '',
String? confirmText = '确认',
String? cancelText = '撤销',
Color? confirmColor,
Color? cancelColor,
void Function(int index, dynamic item)? onValueChanged,
mutli-picker
多列picker
,根据需求运用,作用如下所示
跟 picker
相似,只不过传递的数据源为多维数组
(主张不超过6
组),成果回来:数据源(pickerList)、索引(indexs)、挑选的列表(selectList)
showCupertinoMutliPicker(
// context: context,
pickerList: [
[100, 200, 300, 400, 500],
[10, 20, 30, 40, 50],
[1, 2, 3, 4, 5],
],
onValueChanged: (indexs) {
print(indexs);
},
).then((res) {
setState(() {
value = res.selectList.join(',');
});
print(res);
});
参数如下所示,跟 picker
相似
BuildContext? context, //如果没设置大局,需求传递自己的context
required List<List> pickerList,
List<int>? defaultIndex,
List<String>? units, //单位,结尾是否运用单位,传入就显现
String? title = '',
String? confirmText = '确认',
String? cancelText = '撤销',
Color? confirmColor,
Color? cancelColor,
double? itemFontSize,
void Function(List<int> index)? onValueChanged, //因为pickerList外面传递进来的,因而里面不在回来其他
date-picker
时刻挑选器 date-picker
,一个挑选日期时刻的挑选器,能够设置日期区间,因为是直接展示弹窗的方法,不便于修正,直接运用新的 mutli-picker
编写的,而不是根据前面的 mutli-picker
,也间接提高了功率
运用作用如下所示,成果回来:数据源(pickerList)、索引(indexs)、挑选的列表(selectList),拼接的日期字符串(dateString)
void showIosStyleDatePicker(DatePickerType pickerMode) {
showCupertinoDatePicker(
// context: context,
pickerMode: pickerMode,
beforeYearsInterval: 10,
afterYearsInterval: 10,
// minDate: DateTime.now().subtract(const Duration(days: 1000)),
// maxDate: DateTime.now().add(const Duration(days: 1000)),
).then((res) {
print(res);
setState(() {
value = res.dateString;
});
});
}
运用如下所示,其间 units
为单位,时刻距离看下面参数即可,都是成双成对出现,要不都不传递运用默许
BuildContext? context, //如果没设置大局,需求传递自己的context
DatePickerType pickerMode = DatePickerType.dateTimeMinute,
List<int>? defaultIndexs,
List<String>? units = const [], //如果想运用自己的单位正常传递,如果不显现默许单位传递null即可
DateTime? dateTime, //传入时刻,能够操控默许挑选位置,默许当时时刻
DateTime? minDate, //能够设置前后截止时刻,优先该特点
DateTime? maxDate,
int? beforeYearsInterval, //根据当时时刻或者传入时刻向前向后多少年,默许前后20年,以一年365天为基准
int? afterYearsInterval,
String title = '',
confirmText = '确认',
cancelText = '撤销',
double? itemFontSize,
Color? confirmColor,
Color? cancelColor,
menu(用的不多也介绍一下)
看一下作用图
运用如下所示,外面需求放一个 Container
或 SizeBox
操控外部点击的按钮大小,显现作用就如上所示,因为运用比较少,且功用比较简略,定制多余,就不封装了
CupertinoContextMenu(
actions: <Widget>[
CupertinoContextMenuAction(
onPressed: () {
Navigator.pop(context);
},
isDefaultAction: true,
trailingIcon: CupertinoIcons.doc_on_clipboard_fill,
child: const Text('Copy'),
),
CupertinoContextMenuAction(
onPressed: () {
Navigator.pop(context);
},
trailingIcon: CupertinoIcons.share,
child: const Text('Share'),
),
CupertinoContextMenuAction(
onPressed: () {
Navigator.pop(context);
},
trailingIcon: CupertinoIcons.heart,
child: const Text('Favorite'),
),
CupertinoContextMenuAction(
onPressed: () {
Navigator.pop(context);
},
isDestructiveAction: true,
trailingIcon: CupertinoIcons.delete,
child: const Text('Delete'),
),
],
child: Container(
decoration: BoxDecoration(
color: CupertinoColors.systemYellow,
borderRadius: BorderRadius.circular(20.0),
),
child: const FlutterLogo(size: 500.0),
),
不传递context方法弹出dialog
事例运用的是计划一,预留该类,方便后续添加设置特点(事例中并没有扩展其他特点,仅仅预留运用)
计划一 InheritedWidget
+ navigatorKey
咱们运用 InheritedWidget
+ navigatorKey
来解决即可
InheritedWidget
设置,为了方便运用,运用了静态变量
class DialogConfig extends InheritedWidget {
static GlobalKey<NavigatorState>? globalNavigatorKey;
const DialogConfig.internal({
Key? key,
required Widget child,
}) : super(key: key, child: child);
factory DialogConfig({
Key? key,
required GlobalKey<NavigatorState> globalNavigatorKey,
required Widget child,
}) {
DialogConfig.globalNavigatorKey = globalNavigatorKey;
return DialogConfig.internal(
key: key,
child: child,
);
}
/*
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
DialogConfig(
globalNavigatorKey: navigatorKey,
child: MaterialApp(
navigatorKey: navigatorKey,
...,
),
);
*/
static BuildContext get context {
assert(globalNavigatorKey?.currentState?.context != null, '获取globalcontext失败,请在main函数中的 MaterialApp 外层,设置 DialogConfig,且传入MaterialApp的navigatorKey');
return globalNavigatorKey!.currentState!.context;
}
//就像 Navigator.of(context)一样获取,用于调用内部参数
static DialogConfig of(BuildContext context) {
final DialogConfig? result = context.dependOnInheritedWidgetOfExactType<DialogConfig>();
assert(result != null, 'No DialogConfig found in context');
return result!;
}
@override
bool updateShouldNotify(DialogConfig oldWidget) {
return false;
}
}
MaterialApp 所在类
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
DialogConfig(
globalNavigatorKey: navigatorKey,
child: MaterialApp(
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
),
);
计划二 单例类 + navigatorKey(简略粗犷)
创立一个保存单例类
, 然后 MaterialApp
所在出往单例类传入navigatorKey
参数即可
class DialigConfig {
static GlobalKey<NavigatorState>? globalNavigatorKey;
static BuildContext get context {
assert(globalNavigatorKey?.currentState?.context != null, '获取globalcontext失败,请在 MaterialApp 所在处,设置 DialogConfig,且传入MaterialApp的navigatorKey');
return globalNavigatorKey!.currentState!.context;
}
}
然后直接传入即可
DialigConfig1.globalNavigatorKey = navigatorKey;
最后
能够直接拉进自己的代码仓库运用,快来试试吧