diff --git a/example/lib/main.dart b/example/lib/main.dart index e8ba6fd..8985acb 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -44,11 +44,11 @@ class _MyAppState extends State { // The StatefulWidget's job is to take in some data and create a State class. // In this case, the Widget takes a title, and creates a _MyHomePageState. class MyHomePage extends StatefulWidget { - final String title; + final String? title; - final VoidCallback onSetting; + final VoidCallback? onSetting; - MyHomePage({Key key, this.title, this.onSetting}) : super(key: key); + MyHomePage({Key? key, this.title, this.onSetting}) : super(key: key); @override _MyHomePageState createState() => _MyHomePageState(); @@ -59,23 +59,23 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { // Whether the green box should be visible or invisible - String selectedIndexText; + String? selectedIndexText; - int selectIdx; + int? selectIdx; - String singleSelectedIndexText; + String? singleSelectedIndexText; - int selectIndex; + int? selectIndex; - String multiSelectedIndexesText; + String? multiSelectedIndexesText; - List selectedIndexes; + List? selectedIndexes; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text(widget.title), + title: Text(widget.title ??''), actions: [ IconButton( icon: Icon(Icons.settings), @@ -683,7 +683,7 @@ class _MyHomePageState extends State { ), ListTile( title: Text( - "List dialog ${selectedIndexText != null && selectedIndexText.isNotEmpty ? '(index:' + selectedIndexText + ')' : ''}", + "List dialog ${selectedIndexText != null && selectedIndexText!.isNotEmpty ? '(index:' + selectedIndexText! + ')' : ''}", ), onTap: () async { int index = await showAnimatedDialog( @@ -722,7 +722,7 @@ class _MyHomePageState extends State { ), ListTile( title: Text( - "List single select${singleSelectedIndexText != null && singleSelectedIndexText.isNotEmpty ? '(index:' + singleSelectedIndexText + ')' : ''}", + "List single select${singleSelectedIndexText != null && singleSelectedIndexText!.isNotEmpty ? '(index:' + singleSelectedIndexText! + ')' : ''}", ), onTap: () async { int index = await showAnimatedDialog( @@ -733,7 +733,7 @@ class _MyHomePageState extends State { titleText: 'Title', listType: ListType.singleSelect, activeColor: Colors.red, - selectedIndex: selectIndex, + selectedIndex: selectIndex!, dataList: List.generate( 20, (index) { @@ -759,7 +759,7 @@ class _MyHomePageState extends State { ), ListTile( title: Text( - "List multiple select${multiSelectedIndexesText != null && multiSelectedIndexesText.isNotEmpty ? '(index:' + multiSelectedIndexesText + ')' : ''}", + "List multiple select${multiSelectedIndexesText != null && multiSelectedIndexesText!.isNotEmpty ? '(index:' + multiSelectedIndexesText! + ')' : ''}", ), onTap: () async { List indexes = await showAnimatedDialog( @@ -769,7 +769,7 @@ class _MyHomePageState extends State { return ClassicListDialogWidget( titleText: 'Title', listType: ListType.multiSelect, - selectedIndexes: selectedIndexes, + selectedIndexes: selectedIndexes!, activeColor: Colors.green, dataList: List.generate( 20, @@ -788,7 +788,7 @@ class _MyHomePageState extends State { print('selectedIndex:${selectedIndexes?.toString()}'); setState(() { this.multiSelectedIndexesText = - selectedIndexes != null && selectedIndexes.length > 0 + selectedIndexes != null && selectedIndexes!.length > 0 ? selectedIndexes.toString() : ''; }); diff --git a/example/lib/model/list_data_model.dart b/example/lib/model/list_data_model.dart index 51ec3be..45db789 100644 --- a/example/lib/model/list_data_model.dart +++ b/example/lib/model/list_data_model.dart @@ -1,15 +1,15 @@ ///List data model class ListDataModel { ///Name - String name; + String? name; ///Value - String value; + String? value; ListDataModel({this.name, this.value}); @override String toString() { - return name; + return name?? ''; } } diff --git a/lib/src/animated_dialog.dart b/lib/src/animated_dialog.dart index 29749ea..ec9cf95 100644 --- a/lib/src/animated_dialog.dart +++ b/lib/src/animated_dialog.dart @@ -7,82 +7,45 @@ import 'package:flutter/services.dart'; import 'custom_dialog_transitions.dart'; -///Is dialog showing +/// Is dialog showing bool isShowing = false; enum DialogTransitionType { - ///Fade animation fade, - - ///Slide from top animation slideFromTop, - - ///Slide from top fade animation slideFromTopFade, - - ///Slide from bottom animation slideFromBottom, - - ///Slide from bottom fade animation slideFromBottomFade, - - ///Slide from left animation slideFromLeft, - - ///Slide from left fade animation slideFromLeftFade, - - ///Slide from right animation slideFromRight, - - ///Slide from right fade animation slideFromRightFade, - - ///Scale animation scale, - - ///Fade scale animation fadeScale, - - ///Rotation animation rotate, - - ///Scale rotate animation scaleRotate, - - ///Fade rotate animation fadeRotate, - - ///3D Rotation animation rotate3D, - - ///Size animation size, - - ///Size fade animation sizeFade, - - ///No animation none, } /// Displays a Material dialog above the current contents of the app -Future showAnimatedDialog({ - @required BuildContext context, +Future showAnimatedDialog({ + required BuildContext context, bool barrierDismissible = false, - @required WidgetBuilder builder, - animationType = DialogTransitionType.fade, + required WidgetBuilder builder, + DialogTransitionType animationType = DialogTransitionType.fade, Curve curve = Curves.linear, - Duration duration, - AlignmentGeometry alignment = Alignment.center, - Axis axis, + Duration? duration, + AlignmentGeometry? alignment, + Axis? axis, }) { - assert(builder != null); - assert(debugCheckHasMaterialLocalizations(context)); - final ThemeData theme = Theme.of(context); - isShowing = true; + final Alignment safeAlignment = (alignment ?? Alignment.center) as Alignment; + return showGeneralDialog( context: context, pageBuilder: (BuildContext buildContext, Animation animation, @@ -90,261 +53,168 @@ Future showAnimatedDialog({ final Widget pageChild = Builder(builder: builder); return SafeArea( top: false, - child: Builder(builder: (BuildContext context) { - return theme != null - ? Theme(data: theme, child: pageChild) - : pageChild; - }), + child: + Builder(builder: (context) => Theme(data: theme, child: pageChild)), ); }, barrierDismissible: barrierDismissible, barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, - barrierColor: Colors.black54, + barrierColor: Colors.black.withOpacity(0.8), transitionDuration: duration ?? const Duration(milliseconds: 400), transitionBuilder: (BuildContext context, Animation animation, Animation secondaryAnimation, Widget child) { switch (animationType) { case DialogTransitionType.fade: return FadeTransition(opacity: animation, child: child); - break; case DialogTransitionType.slideFromRight: return SlideTransition( transformHitTests: false, - position: Tween( - begin: const Offset(1.0, 0.0), - end: Offset.zero, - ).chain(CurveTween(curve: curve)).animate(animation), + position: Tween(begin: const Offset(1, 0), end: Offset.zero) + .chain(CurveTween(curve: curve)) + .animate(animation), child: child, ); - break; case DialogTransitionType.slideFromLeft: return SlideTransition( transformHitTests: false, - position: Tween( - begin: const Offset(-1.0, 0.0), - end: Offset.zero, - ).chain(CurveTween(curve: curve)).animate(animation), + position: + Tween(begin: const Offset(-1, 0), end: Offset.zero) + .chain(CurveTween(curve: curve)) + .animate(animation), child: child, ); - break; case DialogTransitionType.slideFromRightFade: return SlideTransition( - position: Tween( - begin: const Offset(1.0, 0.0), - end: Offset.zero, - ).chain(CurveTween(curve: curve)).animate(animation), - child: FadeTransition( - opacity: animation, - child: child, - ), + position: Tween(begin: const Offset(1, 0), end: Offset.zero) + .chain(CurveTween(curve: curve)) + .animate(animation), + child: FadeTransition(opacity: animation, child: child), ); - break; case DialogTransitionType.slideFromLeftFade: return SlideTransition( - position: Tween( - begin: const Offset(-1.0, 0.0), - end: Offset.zero, - ).chain(CurveTween(curve: curve)).animate(animation), - child: FadeTransition( - opacity: animation, - child: child, - ), + position: + Tween(begin: const Offset(-1, 0), end: Offset.zero) + .chain(CurveTween(curve: curve)) + .animate(animation), + child: FadeTransition(opacity: animation, child: child), ); - break; case DialogTransitionType.slideFromTop: return SlideTransition( transformHitTests: false, - position: Tween( - begin: const Offset(0.0, -1.0), - end: Offset.zero, - ).chain(CurveTween(curve: curve)).animate(animation), + position: + Tween(begin: const Offset(0, -1), end: Offset.zero) + .chain(CurveTween(curve: curve)) + .animate(animation), child: child, ); - break; case DialogTransitionType.slideFromTopFade: return SlideTransition( - position: Tween( - begin: const Offset(0.0, -1.0), - end: Offset.zero, - ).chain(CurveTween(curve: curve)).animate(animation), - child: FadeTransition( - opacity: animation, - child: child, - ), + position: + Tween(begin: const Offset(0, -1), end: Offset.zero) + .chain(CurveTween(curve: curve)) + .animate(animation), + child: FadeTransition(opacity: animation, child: child), ); - break; case DialogTransitionType.slideFromBottom: return SlideTransition( transformHitTests: false, - position: Tween( - begin: const Offset(0.0, 1.0), - end: Offset.zero, - ).chain(CurveTween(curve: curve)).animate(animation), + position: Tween(begin: const Offset(0, 1), end: Offset.zero) + .chain(CurveTween(curve: curve)) + .animate(animation), child: child, ); - break; case DialogTransitionType.slideFromBottomFade: return SlideTransition( - position: Tween( - begin: const Offset(0.0, 1.0), - end: Offset.zero, - ).chain(CurveTween(curve: curve)).animate(animation), - child: FadeTransition( - opacity: animation, - child: child, - ), + position: Tween(begin: const Offset(0, 1), end: Offset.zero) + .chain(CurveTween(curve: curve)) + .animate(animation), + child: FadeTransition(opacity: animation, child: child), ); - break; case DialogTransitionType.scale: return ScaleTransition( - alignment: alignment, - scale: CurvedAnimation( - parent: animation, - curve: Interval( - 0.00, - 0.50, - curve: curve, - ), - ), - child: child, - ); - break; + alignment: safeAlignment, + scale: CurvedAnimation( + parent: animation, curve: Interval(0.0, 0.5, curve: curve)), + child: child); case DialogTransitionType.fadeScale: return ScaleTransition( - alignment: alignment, + alignment: safeAlignment, scale: CurvedAnimation( - parent: animation, - curve: Interval( - 0.00, - 0.50, - curve: curve, - ), - ), + parent: animation, curve: Interval(0.0, 0.5, curve: curve)), child: FadeTransition( - opacity: CurvedAnimation( - parent: animation, - curve: curve, - ), - child: child, - ), + opacity: CurvedAnimation(parent: animation, curve: curve), + child: child), ); - break; case DialogTransitionType.scaleRotate: return ScaleTransition( - alignment: alignment, + alignment: safeAlignment, scale: CurvedAnimation( - parent: animation, - curve: Interval( - 0.00, - 0.50, - curve: curve, - ), - ), + parent: animation, curve: Interval(0.0, 0.5, curve: curve)), child: CustomRotationTransition( - alignment: alignment, + alignment: safeAlignment, turns: Tween(begin: 1, end: 2).animate(CurvedAnimation( parent: animation, curve: Interval(0.0, 1.0, curve: curve))), child: child, ), ); - break; case DialogTransitionType.rotate: return CustomRotationTransition( - alignment: alignment, + alignment: safeAlignment, turns: Tween(begin: 1, end: 2).animate(CurvedAnimation( parent: animation, curve: Interval(0.0, 1.0, curve: curve))), child: child, ); - break; case DialogTransitionType.fadeRotate: return CustomRotationTransition( - alignment: alignment, + alignment: safeAlignment, turns: Tween(begin: 1, end: 2).animate(CurvedAnimation( parent: animation, curve: Interval(0.0, 1.0, curve: curve))), child: FadeTransition( - opacity: CurvedAnimation( - parent: animation, - curve: curve, - ), - child: child, - ), + opacity: CurvedAnimation(parent: animation, curve: curve), + child: child), ); - break; case DialogTransitionType.rotate3D: return Rotation3DTransition( - alignment: alignment, - turns: Tween(begin: math.pi, end: 2.0 * math.pi).animate( + alignment: safeAlignment, + turns: Tween(begin: math.pi, end: 2 * math.pi).animate( CurvedAnimation( parent: animation, curve: Interval(0.0, 1.0, curve: curve))), child: FadeTransition( - opacity: Tween(begin: 0.0, end: 1.0).animate( - CurvedAnimation( - parent: animation, - curve: Interval(0.5, 1.0, curve: Curves.elasticOut))), + opacity: Tween(begin: 0, end: 1).animate(CurvedAnimation( + parent: animation, + curve: Interval(0.5, 1.0, curve: Curves.elasticOut))), child: child, ), ); - break; case DialogTransitionType.size: return Align( - alignment: alignment ?? Alignment.center, - child: SizeTransition( - sizeFactor: CurvedAnimation( - parent: animation, - curve: curve, - ), - axis: axis ?? Axis.vertical, - child: child, - ), - ); - break; + alignment: safeAlignment, + child: SizeTransition( + sizeFactor: CurvedAnimation(parent: animation, curve: curve), + axis: axis ?? Axis.vertical, + child: child)); case DialogTransitionType.sizeFade: return Align( - alignment: alignment ?? Alignment.center, + alignment: safeAlignment, child: SizeTransition( - sizeFactor: CurvedAnimation( - parent: animation, - curve: curve, - ), + sizeFactor: CurvedAnimation(parent: animation, curve: curve), child: FadeTransition( - opacity: CurvedAnimation( - parent: animation, - curve: curve, - ), - child: child, - ), + opacity: CurvedAnimation(parent: animation, curve: curve), + child: child), ), ); - break; case DialogTransitionType.none: return child; - break; - default: - return FadeTransition(opacity: animation, child: child); } }, ); } -/// -///created time: 2019-07-19 14:35 -///author linzhiliang -///version 1.0 -///since -///file name: animated_dialog.dart -///description: Custom dialog widget -/// +/// CustomDialogWidget with null safety class CustomDialogWidget extends StatelessWidget { - /// Creates an alert dialog. - /// - /// Typically used in conjunction with [showDialog]. - /// - /// The [contentPadding] must not be null. The [titlePadding] defaults to - /// null, which implies a default that depends on the values of the other - /// properties. See the documentation of [titlePadding] for details. const CustomDialogWidget({ - Key key, + Key? key, this.title, this.titlePadding, this.titleTextStyle, @@ -358,105 +228,28 @@ class CustomDialogWidget extends StatelessWidget { this.semanticLabel, this.shape, this.minWidth, - }) : assert(contentPadding != null), - super(key: key); - - /// The (optional) title of the dialog is displayed in a large font at the top - /// of the dialog. - /// - /// Typically a [Text] widget. - final Widget title; - - /// Padding around the title. - /// - /// If there is no title, no padding will be provided. Otherwise, this padding - /// is used. - /// - /// This property defaults to providing 24 pixels on the top, left, and right - /// of the title. If the [content] is not null, then no bottom padding is - /// provided (but see [contentPadding]). If it _is_ null, then an extra 20 - /// pixels of bottom padding is added to separate the [title] from the - /// [actions]. - final EdgeInsetsGeometry titlePadding; - - /// Style for the text in the [title] of this [AlertDialog]. - /// - /// If null, [DialogTheme.titleTextStyle] is used, if that's null, defaults to - /// [ThemeData.textTheme.title]. - final TextStyle titleTextStyle; - - /// The (optional) content of the dialog is displayed in the center of the - /// dialog in a lighter font. - /// - /// Typically this is a [SingleChildScrollView] that contains the dialog's - /// message. As noted in the [AlertDialog] documentation, it's important - /// to use a [SingleChildScrollView] if there's any risk that the content - /// will not fit. - final Widget content; - - /// Padding around the content. - /// - /// If there is no content, no padding will be provided. Otherwise, padding of - /// 20 pixels is provided above the content to separate the content from the - /// title, and padding of 24 pixels is provided on the left, right, and bottom - /// to separate the content from the other edges of the dialog. - final EdgeInsetsGeometry contentPadding; - - /// Style for the text in the [content] of this [AlertDialog]. - /// - /// If null, [DialogTheme.contentTextStyle] is used, if that's null, defaults - /// to [ThemeData.textTheme.subhead]. - final TextStyle contentTextStyle; - - /// The (optional) set of actions that are displayed at the bottom of the - /// dialog. - /// - /// Typically this is a list of [FlatButton] widgets. - /// - /// These widgets will be wrapped in a [ButtonBar], which introduces 8 pixels - /// of padding on each side. - /// - /// If the [title] is not null but the [content] _is_ null, then an extra 20 - /// pixels of padding is added above the [ButtonBar] to separate the [title] - /// from the [actions]. - final List actions; - - ///Widget in the bottom - final Widget bottomWidget; - - /// {@macro flutter.material.dialog.backgroundColor} - final Color backgroundColor; - - /// {@macro flutter.material.dialog.elevation} - /// {@macro flutter.material.material.elevation} - final double elevation; - - /// The semantic label of the dialog used by accessibility frameworks to - /// announce screen transitions when the dialog is opened and closed. - /// - /// If this label is not provided, a semantic label will be inferred from the - /// [title] if it is not null. If there is no title, the label will be taken - /// from [MaterialLocalizations.alertDialogLabel]. - /// - /// See also: - /// - /// * [SemanticsConfiguration.isRouteName], for a description of how this - /// value is used. - final String semanticLabel; + }) : super(key: key); - /// {@macro flutter.material.dialog.shape} - final ShapeBorder shape; - - ///Min width - final double minWidth; + final Widget? title; + final EdgeInsetsGeometry? titlePadding; + final TextStyle? titleTextStyle; + final Widget? content; + final EdgeInsetsGeometry? contentPadding; + final TextStyle? contentTextStyle; + final List? actions; + final Widget? bottomWidget; + final Color? backgroundColor; + final double? elevation; + final String? semanticLabel; + final ShapeBorder? shape; + final double? minWidth; @override Widget build(BuildContext context) { - assert(debugCheckHasMaterialLocalizations(context)); final ThemeData theme = Theme.of(context); final DialogTheme dialogTheme = DialogTheme.of(context); final List children = []; - String label = semanticLabel; + String? label = semanticLabel; if (title != null) { children.add(Padding( @@ -465,48 +258,26 @@ class CustomDialogWidget extends StatelessWidget { child: DefaultTextStyle( style: titleTextStyle ?? dialogTheme.titleTextStyle ?? - theme.textTheme.headline6, + theme.textTheme.titleLarge!, child: Semantics( - child: title, namesRoute: true, container: true, + child: title!, ), ), )); - } else { - switch (defaultTargetPlatform) { - case TargetPlatform.iOS: - label = semanticLabel; - break; - case TargetPlatform.android: - case TargetPlatform.fuchsia: - label = semanticLabel ?? - MaterialLocalizations.of(context)?.alertDialogLabel; - break; - case TargetPlatform.linux: - label = semanticLabel ?? - MaterialLocalizations.of(context)?.alertDialogLabel; - break; - case TargetPlatform.macOS: - label = semanticLabel; - break; - case TargetPlatform.windows: - label = semanticLabel ?? - MaterialLocalizations.of(context)?.alertDialogLabel; - break; - } } if (content != null) { children.add( Flexible( child: Padding( - padding: contentPadding, + padding: contentPadding!, child: DefaultTextStyle( style: contentTextStyle ?? dialogTheme.contentTextStyle ?? - theme.textTheme.subtitle1, - child: content, + theme.textTheme.bodyMedium!, + child: content!, ), ), ), @@ -514,13 +285,13 @@ class CustomDialogWidget extends StatelessWidget { } if (bottomWidget != null) { - children.add(bottomWidget); + children.add(bottomWidget!); } else if (actions != null) { children.add( ButtonBarTheme( data: ButtonBarTheme.of(context), child: ButtonBar( - children: actions, + children: actions!, ), ), ); @@ -534,48 +305,36 @@ class CustomDialogWidget extends StatelessWidget { ), ); - if (label != null) + if (label != null) { dialogChild = Semantics( namesRoute: true, label: label, child: dialogChild, ); + } dialogChild = CustomDialog( backgroundColor: backgroundColor, elevation: elevation, - minWidth: minWidth, + minWidth: minWidth ?? 280.0, shape: shape, child: dialogChild, ); return AnnotatedRegion( - value: SystemUiOverlayStyle.light.copyWith( - statusBarIconBrightness: Brightness.light, - statusBarColor: Colors.transparent), - child: dialogChild); + value: SystemUiOverlayStyle.light.copyWith( + statusBarIconBrightness: Brightness.light, + statusBarColor: Colors.transparent, + ), + child: dialogChild, + ); } } -/// A material design dialog. -/// -/// This dialog widget does not have any opinion about the contents of the -/// dialog. Rather than using this widget directly, consider using [AlertDialog] -/// or [SimpleDialog], which implement specific kinds of material design -/// dialogs. -/// -/// See also: -/// -/// * [AlertDialog], for dialogs that have a message and some buttons. -/// * [SimpleDialog], for dialogs that offer a variety of options. -/// * [showDialog], which actually displays the dialog and returns its result. -/// * +/// CustomDialog with null safety class CustomDialog extends StatelessWidget { - /// Creates a dialog. - /// - /// Typically used in conjunction with [showDialog]. const CustomDialog({ - Key key, + Key? key, this.backgroundColor, this.elevation, this.insetAnimationDuration = const Duration(milliseconds: 100), @@ -585,54 +344,14 @@ class CustomDialog extends StatelessWidget { this.child, }) : super(key: key); - /// {@template flutter.material.dialog.backgroundColor} - /// The background color of the surface of this [Dialog]. - /// - /// This sets the [Material.color] on this [Dialog]'s [Material]. - /// - /// If `null`, [ThemeData.cardColor] is used. - /// {@endtemplate} - final Color backgroundColor; - - /// {@template flutter.material.dialog.elevation} - /// The z-coordinate of this [Dialog]. - /// - /// If null then [DialogTheme.elevation] is used, and if that's null then the - /// dialog's elevation is 24.0. - /// {@endtemplate} - /// {@macro flutter.material.material.elevation} - final double elevation; - - /// The duration of the animation to show when the system keyboard intrudes - /// into the space that the dialog is placed in. - /// - /// Defaults to 100 milliseconds. + final Widget? child; + final Color? backgroundColor; + final double? elevation; final Duration insetAnimationDuration; - - /// The curve to use for the animation shown when the system keyboard intrudes - /// into the space that the dialog is placed in. - /// - /// Defaults to [Curves.fastOutSlowIn]. final Curve insetAnimationCurve; - - ///Min width of the dialog final double minWidth; + final ShapeBorder? shape; - /// {@template flutter.material.dialog.shape} - /// The shape of this dialog's border. - /// - /// Defines the dialog's [Material.shape]. - /// - /// The default shape is a [RoundedRectangleBorder] with a radius of 2.0. - /// {@endtemplate} - final ShapeBorder shape; - - /// The widget below this widget in the tree. - /// - /// {@macro flutter.widgets.child} - final Widget child; - - // TODO(johnsonmh): Update default dialog border radius to 4.0 to match material spec. static const RoundedRectangleBorder _defaultDialogShape = RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(2.0))); @@ -654,7 +373,7 @@ class CustomDialog extends StatelessWidget { context: context, child: Center( child: ConstrainedBox( - constraints: BoxConstraints(minWidth: minWidth ?? 280.0), + constraints: BoxConstraints(minWidth: minWidth), child: Material( color: backgroundColor ?? dialogTheme.backgroundColor ?? @@ -663,7 +382,7 @@ class CustomDialog extends StatelessWidget { elevation ?? dialogTheme.elevation ?? _defaultElevation, shape: shape ?? dialogTheme.shape ?? _defaultDialogShape, type: MaterialType.card, - child: child, + child: child ?? const SizedBox.shrink(), ), ), ), diff --git a/lib/src/classic_dialog_widget.dart b/lib/src/classic_dialog_widget.dart index 0b97b2a..ea0e767 100644 --- a/lib/src/classic_dialog_widget.dart +++ b/lib/src/classic_dialog_widget.dart @@ -1,50 +1,23 @@ import 'package:flutter/material.dart'; import 'package:flutter_animated_dialog/flutter_animated_dialog.dart'; -///Single selection callback of list dialog typedef OnSingleSelectionCallback = void Function(int selectedIndex); - -///Multiple selection callback of list dialog typedef OnMultiSelectionCallback = void Function(List selectedIndexes); -/// -///created time: 2019-07-31 09:35 -///author linzhiliang -///version 1.0 -///since -///file name: classic_dialog_widget.dart -///description: General dialog -/// @immutable class ClassicGeneralDialogWidget extends StatelessWidget { - ///Title text of the dialog - final String titleText; - - ///Content text of the dialog - final String contentText; - - ///Text of negative button, the left button at the bottom of dialog - final String negativeText; - - ///Text of positive button, the right button at the bottom of dialog - final String positiveText; - - ///TextStyle of negative button, the left button at the bottom of dialog - final TextStyle negativeTextStyle; - - ///TextStyle of positive button, the right button at the bottom of dialog - final TextStyle positiveTextStyle; - - ///Click callback of negative button - final VoidCallback onNegativeClick; - - ///Click callback of positive button - final VoidCallback onPositiveClick; - - ///Actions at the bottom of dialog, when this is set, [negativeText] [positiveText] [onNegativeClick] [onPositiveClick] will not work。 - final List actions; - - ClassicGeneralDialogWidget({ + final String? titleText; + final String? contentText; + final String? negativeText; + final String? positiveText; + final TextStyle? negativeTextStyle; + final TextStyle? positiveTextStyle; + final VoidCallback? onNegativeClick; + final VoidCallback? onPositiveClick; + final List? actions; + + const ClassicGeneralDialogWidget({ + Key? key, this.titleText, this.contentText, this.actions, @@ -54,56 +27,47 @@ class ClassicGeneralDialogWidget extends StatelessWidget { this.positiveTextStyle, this.onNegativeClick, this.onPositiveClick, - }); + }) : super(key: key); @override Widget build(BuildContext context) { - // TODO: implement build return CustomDialogWidget( title: titleText != null ? Text( - titleText, - style: Theme.of(context).dialogTheme.titleTextStyle, + titleText!, + style: Theme.of(context).textTheme.titleLarge, ) : null, content: contentText != null ? Text( - contentText, - style: Theme.of(context).dialogTheme.contentTextStyle, + contentText!, + style: Theme.of(context).textTheme.bodyMedium, ) : null, actions: actions ?? [ - onNegativeClick != null - ? FlatButton( - onPressed: onNegativeClick, - splashColor: Theme.of(context).splashColor, - highlightColor: Theme.of(context).highlightColor, - child: Text( - negativeText ?? 'cancel', - style: negativeTextStyle ?? - TextStyle( - color: Theme.of(context).textTheme.overline.color, - fontSize: - Theme.of(context).textTheme.button.fontSize), - ), - ) - : null, - onPositiveClick != null - ? FlatButton( - onPressed: onPositiveClick, - splashColor: Theme.of(context).splashColor, - highlightColor: Theme.of(context).highlightColor, - child: Text( - positiveText ?? 'confirm', - style: positiveTextStyle ?? - TextStyle( - color: Theme.of(context).primaryColor, - fontSize: - Theme.of(context).textTheme.button.fontSize), - ), - ) - : null, + if (onNegativeClick != null) + TextButton( + onPressed: onNegativeClick, + child: Text( + negativeText ?? 'Cancel', + style: negativeTextStyle ?? + TextStyle( + color: Theme.of(context).textTheme.bodySmall?.color, + ), + ), + ), + if (onPositiveClick != null) + TextButton( + onPressed: onPositiveClick, + child: Text( + positiveText ?? 'Confirm', + style: positiveTextStyle ?? + TextStyle( + color: Theme.of(context).primaryColor, + ), + ), + ), ], elevation: 0.0, shape: Theme.of(context).dialogTheme.shape, @@ -111,221 +75,122 @@ class ClassicGeneralDialogWidget extends StatelessWidget { } } -///List type -enum ListType { - ///Single - single, - - ///Single select - singleSelect, +enum ListType { single, singleSelect, multiSelect } - ///Multiple select - multiSelect, -} - -/// -///created time: 2019-08-01 08:59 -///author linzhiliang -///version 1.0 -///since -///file name: classic_dialog_widget.dart -///description: Classic dialog with list content -/// class ClassicListDialogWidget extends StatefulWidget { - ///Title text of the dialog - final String titleText; - - ///Data of the list + final String? titleText; final List dataList; - - ///Custom list item widget - final Widget listItem; - - ///Click callback of default list item - final VoidCallback onListItemClick; - - ///List type + final Widget? listItem; + final VoidCallback? onListItemClick; final ListType listType; - - ///Where to place control relative to the text final ListTileControlAffinity controlAffinity; - - ///The active color of radio or checkbox - final Color activeColor; - - ///Selected indexes when [listType] is [ListType.multiSelect] + final Color? activeColor; final List selectedIndexes; - - ///Selected index when [listType] is [ListType.singleSelect] final int selectedIndex; - - ///Text of negative button, the left button at the bottom of dialog - final String negativeText; - - ///Text of positive button, the right button at the bottom of dialog - final String positiveText; - - ///Click callback of negative button - final VoidCallback onNegativeClick; - - ///Click callback of positive button - final VoidCallback onPositiveClick; - - ///Actions at the bottom of dialog, when this is set, [negativeText] [positiveText] [onNegativeClick] [onPositiveClick] will not work。 - final List actions; - - ClassicListDialogWidget({ + final String? negativeText; + final String? positiveText; + final VoidCallback? onNegativeClick; + final VoidCallback? onPositiveClick; + final List? actions; + + const ClassicListDialogWidget({ + Key? key, this.titleText, - this.dataList, + required this.dataList, this.listItem, this.onListItemClick, this.listType = ListType.single, this.controlAffinity = ListTileControlAffinity.leading, this.activeColor, - this.selectedIndexes, - this.selectedIndex, + this.selectedIndexes = const [], + this.selectedIndex = -1, this.actions, this.negativeText, this.positiveText, this.onNegativeClick, this.onPositiveClick, - }); + }) : super(key: key); @override - State createState() { - // TODO: implement createState - return ClassicListDialogWidgetState(); - } + State> createState() => + _ClassicListDialogWidgetState(); } -class ClassicListDialogWidgetState extends State { - int selectedIndex; - List valueList; - List selectedIndexes = []; +class _ClassicListDialogWidgetState + extends State> { + late int selectedIndex; + late List valueList; + late List selectedIndexes; @override void initState() { super.initState(); - valueList = List.generate(widget.dataList.length, (index) { - if (widget.selectedIndexes != null && - widget.selectedIndexes.contains(index)) { - return true; - } - return false; - }).toList(growable: true); selectedIndex = widget.selectedIndex; - selectedIndexes = widget.selectedIndexes; + selectedIndexes = List.from(widget.selectedIndexes); + valueList = List.generate( + widget.dataList.length, + (index) => widget.selectedIndexes.contains(index), + ); } @override Widget build(BuildContext context) { - // TODO: implement build - Widget contentWidget; - if (widget.dataList != null) { - contentWidget = ListView.builder( - shrinkWrap: true, - itemBuilder: (context, index) { - if (widget.listItem == null) { - switch (widget.listType) { - case ListType.single: - return ListTile( - title: Text( - widget.dataList[index].toString(), - style: Theme.of(context).dialogTheme.contentTextStyle, - ), - onTap: widget.onListItemClick ?? - () { - Navigator.of(context).pop(index); - }, - ); - break; - case ListType.singleSelect: - return RadioListTile( - controlAffinity: widget.controlAffinity, - title: Text( - widget.dataList[index].toString(), - style: Theme.of(context).dialogTheme.contentTextStyle, - ), - activeColor: - widget.activeColor ?? Theme.of(context).primaryColor, - value: index, - groupValue: selectedIndex, - onChanged: (value) { - setState(() { - selectedIndex = value; - }); - }, - ); - break; - case ListType.multiSelect: - return CheckboxListTile( - controlAffinity: widget.controlAffinity, - selected: valueList[index], - value: valueList[index], - title: Text( - widget.dataList[index].toString(), - style: Theme.of(context).dialogTheme.contentTextStyle, - ), - onChanged: (value) { - setState(() { - valueList[index] = value; - }); - }, - activeColor: - widget.activeColor ?? Theme.of(context).primaryColor, - ); - break; - default: - return ListTile( - title: Text( - widget.dataList[index].toString(), - style: Theme.of(context).dialogTheme.contentTextStyle, - ), - onTap: widget.onListItemClick ?? - () { - Navigator.of(context).pop(index); - }, - ); - break; - } - } else { - return widget.listItem; - } - }, - itemCount: widget.dataList.length, - ); - contentWidget = Container( - width: double.maxFinite, - child: contentWidget, - ); - } else {} + Widget contentWidget = ListView.builder( + shrinkWrap: true, + itemCount: widget.dataList.length, + itemBuilder: (context, index) { + if (widget.listItem != null) return widget.listItem!; + switch (widget.listType) { + case ListType.single: + return ListTile( + title: Text( + widget.dataList[index].toString(), + style: Theme.of(context).textTheme.bodyMedium, + ), + onTap: widget.onListItemClick ?? + () => Navigator.of(context).pop(index), + ); + case ListType.singleSelect: + return RadioListTile( + controlAffinity: widget.controlAffinity, + title: Text( + widget.dataList[index].toString(), + style: Theme.of(context).textTheme.bodyMedium, + ), + activeColor: widget.activeColor ?? Theme.of(context).primaryColor, + value: index, + groupValue: selectedIndex, + onChanged: (value) => setState(() => selectedIndex = value!), + ); + case ListType.multiSelect: + return CheckboxListTile( + controlAffinity: widget.controlAffinity, + title: Text( + widget.dataList[index].toString(), + style: Theme.of(context).textTheme.bodyMedium, + ), + value: valueList[index], + onChanged: (value) => setState(() => valueList[index] = value!), + activeColor: widget.activeColor ?? Theme.of(context).primaryColor, + ); + } + }, + ); return CustomDialogWidget( title: widget.titleText != null - ? Text( - widget.titleText, - style: Theme.of(context).dialogTheme.titleTextStyle, - ) + ? Text(widget.titleText!, style: Theme.of(context).textTheme.titleLarge) : null, - contentPadding: EdgeInsets.all(0.0), - content: contentWidget, + content: Container(width: double.maxFinite, child: contentWidget), + contentPadding: const EdgeInsets.all(0), actions: widget.actions ?? [ - widget.onNegativeClick != null - ? FlatButton( - onPressed: widget.onNegativeClick, - splashColor: Theme.of(context).splashColor, - highlightColor: Theme.of(context).highlightColor, - child: Text( - widget.negativeText ?? 'cancel', - style: TextStyle( - color: Theme.of(context).textTheme.overline.color, - fontSize: - Theme.of(context).textTheme.button.fontSize), - ), - ) - : null, - FlatButton( + if (widget.onNegativeClick != null) + TextButton( + onPressed: widget.onNegativeClick, + child: Text(widget.negativeText ?? 'Cancel'), + ), + TextButton( onPressed: widget.onPositiveClick ?? () { switch (widget.listType) { @@ -337,27 +202,17 @@ class ClassicListDialogWidgetState extends State { break; case ListType.multiSelect: selectedIndexes = []; - int length = valueList.length; - for (int i = 0; i < length; i++) { - if (valueList[i]) { - selectedIndexes.add(i); - } + for (int i = 0; i < valueList.length; i++) { + if (valueList[i]) selectedIndexes.add(i); } Navigator.of(context).pop(selectedIndexes); break; } }, - splashColor: Theme.of(context).splashColor, - highlightColor: Theme.of(context).highlightColor, - child: Text( - widget.positiveText ?? 'confirm', - style: TextStyle( - color: Theme.of(context).primaryColor, - fontSize: Theme.of(context).textTheme.button.fontSize), - ), + child: Text(widget.positiveText ?? 'Confirm'), ), ], - elevation: 0.0, + elevation: 0, shape: Theme.of(context).dialogTheme.shape, ); } diff --git a/lib/src/custom_dialog_transitions.dart b/lib/src/custom_dialog_transitions.dart index dabea8c..ec94426 100644 --- a/lib/src/custom_dialog_transitions.dart +++ b/lib/src/custom_dialog_transitions.dart @@ -1,47 +1,19 @@ import 'dart:math' as math; - import 'package:flutter/material.dart'; -/// Animates the rotation of a widget. -/// -/// Here's an illustration of the [RotationTransition] widget, with it's [turns] -/// animated by a [CurvedAnimation] set to [Curves.elasticOut]: -/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/rotation_transition.mp4} -/// See also: -/// -/// * [ScaleTransition], a widget that animates the scale of a transformed -/// widget. -/// * [SizeTransition], a widget that animates its own size and clips and -/// aligns its child. +/// 3D rotation transition (چرخش سه‌بعدی) class Rotation3DTransition extends AnimatedWidget { - /// Creates a rotation transition. - /// - /// The [turns] argument must not be null. - const Rotation3DTransition({ - Key key, - @required Animation turns, + final Alignment alignment; + final Widget? child; + + Rotation3DTransition({ + Key? key, + required Animation turns, this.alignment = Alignment.center, this.child, - }) : assert(turns != null), - super(key: key, listenable: turns); - - /// The animation that controls the rotation of the child. - /// - /// If the current value of the turns animation is v, the child will be - /// rotated v * 2 * pi radians before being painted. - Animation get turns => listenable; - - /// The alignment of the origin of the coordinate system around which the - /// rotation occurs, relative to the size of the box. - /// - /// For example, to set the origin of the rotation to top right corner, use - /// an alignment of (1.0, -1.0) or use [Alignment.topRight] - final Alignment alignment; + }) : super(key: key, listenable: turns); - /// The widget below this widget in the tree. - /// - /// {@macro flutter.widgets.child} - final Widget child; + Animation get turns => listenable as Animation; @override Widget build(BuildContext context) { @@ -51,52 +23,25 @@ class Rotation3DTransition extends AnimatedWidget { ..rotateY(turnsValue); return Transform( transform: transform, - alignment: FractionalOffset(0.5, 0.5), + alignment: alignment, child: child, ); } } -/// Animates the rotation of a widget. -/// -/// Here's an illustration of the [RotationTransition] widget, with it's [turns] -/// animated by a [CurvedAnimation] set to [Curves.elasticOut]: -/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/rotation_transition.mp4} -/// See also: -/// -/// * [ScaleTransition], a widget that animates the scale of a transformed -/// widget. -/// * [SizeTransition], a widget that animates its own size and clips and -/// aligns its child. +/// 2D rotation transition (چرخش عادی با محور Z) class CustomRotationTransition extends AnimatedWidget { - /// Creates a rotation transition. - /// - /// The [turns] argument must not be null. - const CustomRotationTransition({ - Key key, - @required Animation turns, + final Alignment alignment; + final Widget? child; + + CustomRotationTransition({ + Key? key, + required Animation turns, this.alignment = Alignment.center, this.child, - }) : assert(turns != null), - super(key: key, listenable: turns); - - /// The animation that controls the rotation of the child. - /// - /// If the current value of the turns animation is v, the child will be - /// rotated v * 2 * pi radians before being painted. - Animation get turns => listenable; - - /// The alignment of the origin of the coordinate system around which the - /// rotation occurs, relative to the size of the box. - /// - /// For example, to set the origin of the rotation to top right corner, use - /// an alignment of (1.0, -1.0) or use [Alignment.topRight] - final Alignment alignment; + }) : super(key: key, listenable: turns); - /// The widget below this widget in the tree. - /// - /// {@macro flutter.widgets.child} - final Widget child; + Animation get turns => listenable as Animation; @override Widget build(BuildContext context) { diff --git a/pubspec.lock b/pubspec.lock index f190af6..203ecd5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,51 +5,50 @@ packages: dependency: transitive description: name: async - url: "https://pub.flutter-io.cn" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" source: hosted - version: "2.5.0-nullsafety.3" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.flutter-io.cn" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.1" characters: dependency: transitive description: name: characters - url: "https://pub.flutter-io.cn" + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" source: hosted - version: "1.1.0-nullsafety.5" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.0-nullsafety.3" + version: "1.3.0" clock: dependency: transitive description: name: clock - url: "https://pub.flutter-io.cn" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.1" collection: dependency: transitive description: name: collection - url: "https://pub.flutter-io.cn" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" source: hosted - version: "1.15.0-nullsafety.5" + version: "1.18.0" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.flutter-io.cn" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted - version: "1.2.0-nullsafety.3" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -60,27 +59,62 @@ packages: description: flutter source: sdk version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.dev" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" matcher: dependency: transitive description: name: matcher - url: "https://pub.flutter-io.cn" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + url: "https://pub.dev" source: hosted - version: "0.12.10-nullsafety.3" + version: "0.8.0" meta: dependency: transitive description: name: meta - url: "https://pub.flutter-io.cn" + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + url: "https://pub.dev" source: hosted - version: "1.3.0-nullsafety.6" + version: "1.12.0" path: dependency: transitive description: name: path - url: "https://pub.flutter-io.cn" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" source: hosted - version: "1.8.0-nullsafety.3" + version: "1.9.0" sky_engine: dependency: transitive description: flutter @@ -90,57 +124,66 @@ packages: dependency: transitive description: name: source_span - url: "https://pub.flutter-io.cn" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" source: hosted - version: "1.8.0-nullsafety.4" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.flutter-io.cn" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" source: hosted - version: "1.10.0-nullsafety.6" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.flutter-io.cn" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.flutter-io.cn" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.flutter-io.cn" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted - version: "1.2.0-nullsafety.3" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.flutter-io.cn" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + url: "https://pub.dev" source: hosted - version: "0.2.19-nullsafety.6" - typed_data: + version: "0.7.0" + vector_math: dependency: transitive description: - name: typed_data - url: "https://pub.flutter-io.cn" + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "1.3.0-nullsafety.5" - vector_math: + version: "2.1.4" + vm_service: dependency: transitive description: - name: vector_math - url: "https://pub.flutter-io.cn" + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + url: "https://pub.dev" source: hosted - version: "2.1.0-nullsafety.5" + version: "14.2.1" sdks: - dart: ">=2.12.0-0.0 <3.0.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index c8b1ef4..86b2aee 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/JackJonson/flutter_animated_dialog email: yswing1@gmail.com environment: - sdk: ">=2.1.0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: flutter: