Created
December 10, 2019 09:57
-
-
Save tarek360/9008eca7f817e6bf9b53a5304c3c772f to your computer and use it in GitHub Desktop.
A TopAlert to show at the top of any page (widget).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
void main() => runApp(MyApp()); | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Top Alert View', | |
theme: ThemeData( | |
primarySwatch: Colors.blue, | |
), | |
home: MyHomePage(), | |
); | |
} | |
} | |
class MyHomePage extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text('Flutter Top Alert View'), | |
), | |
body: TopAlert( | |
child: Center( | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: <Widget>[ | |
Text( | |
'Push the button to show an Alert!', | |
), | |
MyButton() | |
], | |
), | |
), | |
), | |
); | |
} | |
} | |
class MyButton extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return IconButton( | |
color: Colors.red, | |
iconSize: 56, | |
icon: Icon(Icons.info), | |
onPressed: () { | |
TopAlert.of(context).showAlert( | |
Container( | |
color: Colors.red, | |
child: Center( | |
child: Text( | |
'Something went wrong!', | |
style: Theme.of(context) | |
.textTheme | |
.subhead | |
.copyWith(color: Colors.white), | |
), | |
), | |
), | |
); | |
}, | |
); | |
} | |
} | |
// Alert Widget | |
class TopAlert extends StatefulWidget { | |
final Widget child; | |
final Duration animationDuration; | |
final Duration displayDuration; | |
TopAlert({ | |
@required this.child, | |
this.animationDuration: const Duration(milliseconds: 200), | |
this.displayDuration: const Duration(seconds: 2), | |
}); | |
@override | |
TopAlertState createState() => TopAlertState(); | |
static TopAlertState of(BuildContext context, {bool nullOk = false}) { | |
assert(nullOk != null); | |
assert(context != null); | |
final TopAlertState result = | |
context.findAncestorStateOfType<TopAlertState>(); | |
if (nullOk || result != null) return result; | |
throw FlutterError.fromParts(<DiagnosticsNode>[ | |
ErrorSummary( | |
'TopAlert.of() called with a context that does not contain a TopAlert.'), | |
ErrorDescription( | |
'No TopAlert ancestor could be found starting from the context that was passed to TopAlert.of(). ' | |
'This usually happens when the context provided is from the same StatefulWidget as that ' | |
'whose build function actually creates the TopAlert widget being sought.'), | |
ErrorHint( | |
'There are several ways to avoid this problem. The simplest is to use a Builder to get a ' | |
'context that is "under" the TopAlert. For an example of this, please see:'), | |
ErrorHint( | |
'A more efficient solution is to split your build function into several widgets. This ' | |
'introduces a new context from which you can obtain the TopAlert. In this solution, ' | |
'you would have an outer widget that creates the TopAlert populated by instances of ' | |
'your new inner widgets, and then in these inner widgets you would use TopAlert.of().\n' | |
'A less elegant but more expedient solution is assign a GlobalKey to the TopAlert, ' | |
'then use the key.currentState property to obtain the TopAlert rather than ' | |
'using the TopAlert.of() function.'), | |
context.describeElement('The context used was') | |
]); | |
} | |
} | |
class TopAlertState extends State<TopAlert> { | |
bool _displayed = false; | |
Widget _alertView; | |
Duration _animationDuration; | |
Duration _displayDuration; | |
@override | |
void initState() { | |
super.initState(); | |
_animationDuration = widget.animationDuration; | |
_displayDuration = widget.displayDuration; | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Stack( | |
children: <Widget>[ | |
widget.child, | |
AnimatedContainer( | |
height: _displayed ? 56 : 0, | |
width: double.infinity, | |
duration: _animationDuration, | |
curve: Curves.easeOutCubic, | |
child: _alertView ?? Container(), | |
onEnd: _delayAndHide, | |
), | |
], | |
); | |
} | |
void showAlert( | |
Widget alertView, { | |
Duration animationDuration, | |
Duration displayDuration, | |
}) { | |
assert(alertView != null); | |
_animationDuration = animationDuration ?? widget.animationDuration; | |
_displayDuration = displayDuration ?? widget.displayDuration; | |
if (!_displayed) { | |
_alertView = alertView; | |
setState(() { | |
_displayed = true; | |
}); | |
} | |
} | |
void _delayAndHide() { | |
if (!_displayed) { | |
return; | |
} | |
Future.delayed(_displayDuration, () { | |
setState(() { | |
_displayed = false; | |
}); | |
// Clear _alertView | |
Future.delayed(_animationDuration, () { | |
_alertView = null; | |
}); | |
}); | |
} | |
} |
Author
tarek360
commented
Dec 10, 2019
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment