Show CircularProgressIndicator while loading URL in Web View - dart

I want to show CircularProgressIndicator when ever the webview loads an URL. Below is code but it only shows loading element while initializing the webview.
Widget build(BuildContext context) {
return new MaterialApp(
theme
: new ThemeData(
primaryColor: Color.fromRGBO(58, 66, 86, 1.0), fontFamily: 'Raleway'),
routes: {
"/": (_) => new WebviewScaffold(
url: url,
appBar: new AppBar(
title: Text(title),
actions: <Widget>[
IconButton(
icon: Icon(Icons.close),
onPressed: () => Navigator.of(context).pop(null),
)
],
),
withJavascript: true,
withLocalStorage: true,
appCacheEnabled: true,
hidden: true,
)
},
);
}
I want it to show loading element when user clicks on any link within webview.

its should work for first time, I know that is not exactly what's your looking for but it may help.
WebviewScaffold(
url: "https://www.google.com/",
appBar: new AppBar(
title: const Text('Widget webview'),
),
withZoom: true,
withLocalStorage: true,
hidden: true,
initialChild: Container(
child: const Center(
child: CircularProgressIndicator(),
),
),
);

This doesn't seem to be supported currently.
There is a pull request that seems to provide such a feature
https://github.com/fluttercommunity/flutter_webview_plugin/pull/255
Several related issues/feature requests
https://github.com/fluttercommunity/flutter_webview_plugin/issues/177
https://github.com/fluttercommunity/flutter_webview_plugin/issues/284
https://github.com/fluttercommunity/flutter_webview_plugin/issues/232
https://github.com/fluttercommunity/flutter_webview_plugin/issues/159

This is how I implemented using IndexedStack
class WebViewWidget extends StatefulWidget {
#override
_WebViewWidgetState createState() => _WebViewWidgetState();
}
class _WebViewWidgetState extends State<WebViewWidget> {
var stackToShow = 1;
#override
Widget build(BuildContext context) {
return IndexedStack(
index: stackToShow,
children: [
WebView(
initialUrl: "https://www.google.com/",
onPageFinished: (String url) {
// when page loaded
setState(() {
stackToShow = 0;
});
},
),
Container(child: Center(child: CircularProgressIndicator())),
],
);
}
}
Enjoy coding!

This will work with WebviewScaffold
Just paste it in your Class.
#override
void initState() {
super.initState();
_onPageProgress =
flutterWebViewPlugin.onProgressChanged.listen(progessChange);
}
progessChange(double event) {
print("Page loading " + event.toString());
if (event == 1.0) {
flutterWebViewPlugin.show();
} else {
flutterWebViewPlugin.hide();
}
}
final flutterWebViewPlugin = FlutterWebviewPlugin();
late StreamSubscription<double> _onPageProgress;
Widget build(BuildContext context) {
return WebviewScaffold(
initialChild: Container(
color: Colors.white,
child: Center(
child: CircularProgressIndicator(
color: Colors.blue,
)),
),
hidden: true,
clearCache: true,
withJavascript: true,
url: "https://www.google.com/",
);
}

Related

Why onDurationChanged is not called in Flutter ios?

I'm using audioplayers: 1.1.0 library to play audio from url
I use the onDurationChanged method to get the audio file length. This is working perfectly on android but in iOS the onDurationChanged is not getting called.
I tried all solutions available in the internet. Noting helps as expected. Your help wil save my day.
Note: If I use
player.play(UrlSource('theme_01.mp3'));
Now works fine. But If I use
player.play(UrlSource('https://www.kozco.com/tech/LRMonoPhase4.mp3');
onDurationChanged is not working
import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart';
import 'dart:async';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Audio Player'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final player = AudioPlayer();
bool isPlaying = false;
Duration duration = Duration.zero;
Duration position = Duration.zero;
String formatTime(int seconds) {
return '${(Duration(seconds: seconds))}'.split('.')[0].padLeft(8, '0');
}
final AudioContext audioContext = AudioContext(
iOS: AudioContextIOS(
defaultToSpeaker: true,
category: AVAudioSessionCategory.ambient,
options: [
AVAudioSessionOptions.defaultToSpeaker,
AVAudioSessionOptions.mixWithOthers,
],
),
android: AudioContextAndroid(
isSpeakerphoneOn: true,
stayAwake: true,
contentType: AndroidContentType.sonification,
usageType: AndroidUsageType.assistanceSonification,
audioFocus: AndroidAudioFocus.none,
),
);
#override
void initState() {
super.initState();
AudioPlayer.global.setGlobalAudioContext(audioContext);
player.onPlayerStateChanged.listen((state) {
setState(() {
isPlaying = state == PlayerState.playing;
});
});
player.onDurationChanged.listen((newDuration) {
setState(() {
duration = newDuration;
});
});
player.onPositionChanged.listen((newPosition) {
setState(() {
position = newPosition;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Slider(
min: 0,
max: duration.inSeconds.toDouble(),
value: position.inSeconds.toDouble(),
onChanged: (value) {
final position = Duration(seconds: value.toInt());
player.seek(position);
player.resume();
},
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircleAvatar(
radius: 25,
child: IconButton(
icon: Icon(
isPlaying ? Icons.pause : Icons.play_arrow,
),
onPressed: () {
if (isPlaying) {
player.pause();
} else {
// player.play(UrlSource('theme_01.mp3'));
// If I use local audio works fine. But If I use the below audio url onDurationChanged is not called.
player.play(UrlSource('https://www.kozco.com/tech/LRMonoPhase4.mp3'));
}
},
),
),
SizedBox(
width: 20,
),
CircleAvatar(
radius: 25,
child: IconButton(
icon: const Icon(Icons.stop),
onPressed: () {
player.stop();
},
),
),
],
),
Container(
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(formatTime(position.inSeconds)),
Text(formatTime((duration - position).inSeconds)),
],
),
),
],
),
),
);
}
}

Flutter: how to remove the white background behind the keyboard when keyboard got activated

I have a flutter app and I do experience a strange behaviour when the keyboard get activated in my iPhone. As you can see from the picture below there is a white background which appears under the keyboard while it is popping up...
Does anyone know how to set that background color???
EDIT:
Thanks for the suggestion to use resizeToAvoidBottomPadding. That command is deprecated and I have replaced with resizeToAvoidBottomInset: false. This has resolved the issue (yup!) and there is no more white background under the keyboard BUT it has created another strange effect. There is now a dark-blue bar above the keyboard. Check the next image... I have set an orange background color so you can see it better. You can also see that the floating button is shifted up which means the dark-blu bar is now between the keyboard and the scaffold. Do you know how I can get rid of that bar? this seems to be worst Thant the white background since it is taking precious screen space..
For reference the scaffold is nested in a CupertinoTabScaffold
Here is the most relevant part of my code
void main() async {
Crashlytics.instance.enableInDevMode = true;
FlutterError.onError = Crashlytics.instance.recordFlutterError;
runApp(ChangeNotifierProvider<Data>(
builder: (context) => Data(),
child: new CupertinoApp(
theme: CupertinoThemeData(
barBackgroundColor: kColorPrimary,
primaryColor:
kColorText,
textTheme: CupertinoTextThemeData(
primaryColor:
kColorText,
navLargeTitleTextStyle: TextStyle(
fontWeight: FontWeight.bold, fontSize: 70.0, color: kColorText),
),
),
home: new CheckIfFirstTime(),
),
));
}
class CheckIfFirstTime extends StatefulWidget {
#override
_CheckIfFirstTimeState createState() => _CheckIfFirstTimeState();
}
class _CheckIfFirstTimeState extends State<CheckIfFirstTime> {
Future checkFirstSeen() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool _seen = (prefs.getBool('seen') ?? false);
if (_seen) {
Navigator.of(context).pushReplacement(
new MaterialPageRoute(builder: (context) => new HomeScreen()));
}
}
#override
void initState() {
super.initState();
checkFirstSeen();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kColorPrimary,
body: Text(
'loading...',
style: kSendButtonTextStyle,
),
);
}
}
class HomeScreen extends StatefulWidget {
static const String id = 'home';
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int currentIndex = 0;
void confirmPlatform() {
if (Platform.isIOS)
appData.isIOS = true;
else
appData.isIOS = false;
}
#override
void initState() {
// TODO: implement initState
super.initState();
confirmPlatform();
}
#override
Widget build(BuildContext context) {
return CupertinoTabScaffold(
resizeToAvoidBottomInset: false,
tabBar: CupertinoTabBar(
backgroundColor: kColorPrimaryLight,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Discover', style: TextStyle(fontFamily: kFontFamily)),
),
BottomNavigationBarItem(
icon: Badge(
showBadge: Provider.of<Data>(context).filterCounter == 0
? false
: true,
badgeContent: Text(
Provider.of<Data>(context).filterCounter.toString(),
style: TextStyle(color: kColorText),
),
child: Icon(Icons.filter_list)),
title: Text('Filters', style: TextStyle(fontFamily: kFontFamily)),
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle),
title: Text('Me', style: TextStyle(fontFamily: kFontFamily)),
),
BottomNavigationBarItem(
icon: Icon(Icons.assignment),
title: Text('Stories', style: TextStyle(fontFamily: kFontFamily)),
),
//with badge
BottomNavigationBarItem(
icon: Badge(
showBadge: Provider.of<Data>(context).basketCounter == '0'
? false
: true,
badgeContent: Text(
Provider.of<Data>(context).basketCounter,
style: TextStyle(color: kColorText),
),
child: Icon(Icons.shopping_cart)),
title: Text('Basket', style: TextStyle(fontFamily: kFontFamily)),
),
],
),
tabBuilder: (context, index) {
if (index == 0) {
return CupertinoTabView(
navigatorKey: firstTabNavKey,
builder: (BuildContext context) => DiscoverScreenFinal(),
);
} else if (index == 1) {
return CupertinoTabView(
navigatorKey: secondTabNavKey,
builder: (BuildContext context) => FilterScreen(),
);
} else if (index == 2) {
return CupertinoTabView(
navigatorKey: thirdTabNavKey,
builder: (BuildContext context) => WelcomeScreen(),
);
} else if (index == 3) {
return CupertinoTabView(
navigatorKey: forthTabNavKey,
builder: (BuildContext context) => Stories(),
);
}
{
return CupertinoTabView(
navigatorKey: fifthTabNavKey,
builder: (BuildContext context) => Basket(),
);
}
},
);
}
}
class DiscoverScreenFinal extends StatefulWidget {
#override
_DiscoverScreenFinalState createState() => _DiscoverScreenFinalState();
}
class _DiscoverScreenFinalState extends State<DiscoverScreenFinal> {
#override
Widget build(BuildContext context) {
return TopBarAgnostic(
text: 'Discover',
style: kSendButtonTextStyle,
firstIcon: Icon(Icons.blur_on),
firstIconDestination: CameraApp(),
scannerOn: true,
secondtIcon: Icon(Icons.location_on),
secondIconDestination: MapPage(),
uniqueHeroTag: 'discover001a',
child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: kColorPrimary,
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButton: FloatingActionButton.extended(
backgroundColor: kColorAccent,
onPressed: () {
//โ€ฆ
},
label: Text(
'To Favorites',
style: kDescriptionTextStyle.copyWith(
fontSize: kFontSizeSmall, fontWeight: FontWeight.bold),
),
icon: Icon(Icons.favorite),
),
body: Container()
),
);
}
}
You have to use this line in Scaffold, it will adjust your view automatically when keyboard is appear and disappear.
resizeToAvoidBottomPadding: false
You could set a backgroundColor to the Scaffold to replace that white background.
This happens because the body of the Scaffold gets resized when you open the keyboard. If you want to avoid the resizing you could set resizeToAvoidBottomInset: false to the Scaffold.
after flutter 2 use this in the Scaffold
resizeToAvoidBottomInset: false,
Best solution is creating a Stack widget with two children: a Container for the background (color, gradient, background image...) and a Scaffold for the app content.
Widget build(BuildContext context) {
return Stack(
children: [
Container(
decoration: /* ... Background styles ... */,
),
Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(backgroundColor: Colors.transparent, elevation: 0),
body: Container(
padding: const EdgeInsets.only(top: 16, left: 16, right: 16),
child: SafeArea(bottom: false, child: /* ... App content ... */))
)
]);
}
Basically you can solve this problem by keeping Scaffold inside of Container, like this:
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment(0.5, 0.3),
colors: [
Colors.red,
Colors.green,
],
),
),
child: Scaffold(/* ... rest of your code ... */)
)
if you are in develop app in java solve this by just adding android:windowSoftInputMode="adjustPan" in your activity present in the manifest file.
Thanks
you can use
child: Scaffold(
backgroundColor: Color(0xff53ccb2),

I'm try use controller of TextField but i receive error "NoSuchMethodError: The method 'call' was called on null"

I'm try use controller of TextField but i receive error
"NoSuchMethodError: The method 'call' was called on null"
It will fine if i use onChange().
My code:
class _MyHomePageState extends State<MyHomePage> {
Icon _searchIcon = Icon(Icons.search, color: Colors.white);
int _searchIconState = 0;
Widget _appBarTitle;
TextEditingController _controller = TextEditingController();
_onChange() {
String text = _controller.text;
print(text);
}
#override
void initState() {
super.initState();
/* My TextField */
_appBarTitle = TextField(
controller: _controller,
onChanged: (text) {
print('onChanged: ' + text);
},
style: TextStyle(color: Colors.white, fontSize: 18),
decoration: InputDecoration(
border: InputBorder.none,
icon: _searchIcon,
hintText: 'Search...',
hintStyle:
TextStyle(color: Colors.white.withOpacity(0.5), fontSize: 18)));
_controller.addListener(_onChange());
}
#override
void dispose() {
// Clean up the controller when the Widget is removed from the Widget tree
// This also removes the _printLatestValue listener
_controller.dispose();
super.dispose();
}
_nestedScrollViewController() {}
_tabBarController() {}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 3,
child: NestedScrollView(
controller: _nestedScrollViewController(),
headerSliverBuilder: (BuildContext context, bool isScrolled) {
return <Widget>[
SliverAppBar(
title: _appBarTitle /* TextField put in here */,
pinned: true,
floating: true,
forceElevated: isScrolled,
bottom: TabBar(
tabs: <Widget>[
Tab(text: 'TO DAY'),
Tab(text: 'TOMORROW'),
Tab(text: '7Days')
],
controller: _tabBarController(),
),
)
];
},
body: Scaffold(
body: TabBarView(
children: <Widget>[
todayUI(),
tomorrowUI(),
weekUI(),
],
),
)
),
),
);
}
}
You added listener incorrect way
You have to remove () after onChange
_controller.addListener(_onChange());
to
_controller.addListener(_onChange);

Flutter: How to show a CircularProgressIndicator before WebView loads the page?

I'm using the webview_fluttter plugin, but I can't find a way to show a CircularProgressIndicator before the webview shows the page...
What's the equivalent of Androids WebViewClient onPageStarted/onPageFinished?
WebView(
initialUrl: url,
onWebViewCreated: (controller) {
},
)
In version 0.3.5 there is 'onPageFinished' callback. You can create WebView container with IndexedStack.
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class _WebViewContainerState extends State < WebViewContainer > {
var _url;
final _key = UniqueKey();
_WebViewContainerState(this._url);
num _stackToView = 1;
void _handleLoad(String value) {
setState(() {
_stackToView = 0;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: IndexedStack(
index: _stackToView,
children: [
Column(
children: < Widget > [
Expanded(
child: WebView(
key: _key,
javascriptMode: JavascriptMode.unrestricted,
initialUrl: _url,
onPageFinished: _handleLoad,
)
),
],
),
Container(
color: Colors.white,
child: Center(
child: CircularProgressIndicator(),
),
),
],
)
);
}
}
class WebViewContainer extends StatefulWidget {
final url;
WebViewContainer(this.url);
#override
createState() => _WebViewContainerState(this.url);
}
This is working properly for me
initState() {
isLoading = true;
};
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: Text("Your Title",centerTitle: true
),
body: Stack(
children: <Widget>[
new WebView(
initialUrl: /* YourUrl*/,
onPageFinished: (_) {
setState(() {
isLoading = false;
});
},
),
isLoading ? Center( child: CircularProgressIndicator()) : Container(),
],
),
);
}
class _WebViewContainerState extends State<WebViewContainer> {
var _url;
final _key = UniqueKey();
bool _isLoadingPage;
Completer<WebViewController> _controller = Completer<WebViewController>();
_WebViewContainerState(this._url);
#override
void initState() {
super.initState();
_isLoadingPage = true;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
new WebView(
key: _key,
initialUrl: _url,
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (webViewCreate) {
_controller.complete(webViewCreate);
},
onPageFinished: (finish) {
setState(() {
_isLoadingPage = false;
});
},
),
_isLoadingPage
? Container(
alignment: FractionalOffset.center,
child: CircularProgressIndicator(),
)
: Container(
color: Colors.transparent,
),
],
),
);
}
}
Optional parameters hidden and initialChild are available so that you can show something else while waiting for the page to load.If you set hidden to true it will show a default CircularProgressIndicator. If you additionally specify a Widget for initialChild you can have it display whatever you like till page-load.
check this page : flutter_webview_plugin
and you can specify what you want to show with initialChild
return new MaterialApp(
title: 'Flutter WebView Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
routes: {
'/': (_) => const MyHomePage(title: 'Flutter WebView Demo'),
'/widget': (_) => new WebviewScaffold(
url: selectedUrl,
appBar: new AppBar(
title: const Text('Widget webview'),
),
withZoom: true,
withLocalStorage: true,
hidden: true,
initialChild: Container(
child: const Center(
child: CircularProgressIndicator(),
),
),
),
},
);
I use a combination of webview_flutter, progress_indicators
Here is a sample working code:
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'dart:async';
import 'package:progress_indicators/progress_indicators.dart';
class ContactUs extends StatefulWidget {
#override
_ContactUsState createState() => _ContactUsState();
}
class _ContactUsState extends State<ContactUs> {
bool vis1 = true;
Size deviceSize;
#override
Widget build(BuildContext context) {
deviceSize = MediaQuery.of(context).size;
final lindicator = Center(
child: AnimatedOpacity(
// If the Widget should be visible, animate to 1.0 (fully visible). If
// the Widget should be hidden, animate to 0.0 (invisible).
opacity: vis1 ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
// The green box needs to be the child of the AnimatedOpacity
child: HeartbeatProgressIndicator(
child: Container(
width: 100.0,
height: 50.0,
padding: EdgeInsets.fromLTRB(35.0,0.0,5.0,0.0),
child: Row(
children: <Widget>[
Icon(
Icons.all_inclusive, color: Colors.white, size: 14.0,),
Text(
"Loading View", style: TextStyle(color: Colors.white, fontSize: 6.0),),
],
),
),
),
),
);
return new Scaffold(
appBar: new AppBar(
title: new Row(
children:<Widget>[
Text('THisApp'),
lindicator,
]),
backgroundColor: Colors.red,
),
body: new Container(
child:WebView(
initialUrl: 'https://cspydo.com.ng/',
javaScriptMode: JavaScriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController){
setState(() {
vis1=false;
});
},
)
),
);
}
}
Found the solution.You can add initialChild and set attribute hidden as true.
WebviewScaffold(
hidden: true,
url:url,initialChild: Center(
child: Text("Plase Wait...",style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.deepPurpleAccent[100]
),)
) )
You can work on isLoading and change it after you are sure data is loaded properly.
class X extends StatefulWidget {
XState createState() => XState();
}
class XState extends State<X>{
bool isLoading = false;
#override
void initState() {
setState(() {
isLoading = true;
});
super.initState();
}
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
isLoading ? Center(child: CircularProgressIndicator()) : WebView(...)
]
)
);
}
}
I used A stack in flutter.
#override
void initState() {
isLoading = true;
super.initState();
}
in the build method
Stack(
children: [
isLoading ? Loading() : Container(),
Container(
child: Flex(
direction: Axis.vertical,
children: [
Expanded(
child: InAppWebView(
contextMenu: contextMenu,
initialUrl: url,
onLoadSop: (InAppWebViewController controller, url) {
setState(() {
isLoading = false;
});
},
)
),
],
),
),
],
)

How to make an AlertDialog in Flutter?

I am learning to build apps in Flutter. Now I have come to alert dialogs. I have done them before in Android and iOS, but how do I make an alert in Flutter?
Here are some related SO questions:
How to style AlertDialog Actions in Flutter
adding dropdown menu in alert dialog box in flutter
Show alert dialog on app main screen load automatically
how to refresh alertdialog in flutter
Alert Dialog with Rounded corners in flutter
I'd like to make a more general canonical Q&A so my answer is below.
One Button
showAlertDialog(BuildContext context) {
// set up the button
Widget okButton = TextButton(
child: Text("OK"),
onPressed: () { },
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Text("My title"),
content: Text("This is my message."),
actions: [
okButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
Two Buttons
showAlertDialog(BuildContext context) {
// set up the buttons
Widget cancelButton = TextButton(
child: Text("Cancel"),
onPressed: () {},
);
Widget continueButton = TextButton(
child: Text("Continue"),
onPressed: () {},
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Text("AlertDialog"),
content: Text("Would you like to continue learning how to use Flutter alerts?"),
actions: [
cancelButton,
continueButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
Three Buttons
showAlertDialog(BuildContext context) {
// set up the buttons
Widget remindButton = TextButton(
child: Text("Remind me later"),
onPressed: () {},
);
Widget cancelButton = TextButton(
child: Text("Cancel"),
onPressed: () {},
);
Widget launchButton = TextButton(
child: Text("Launch missile"),
onPressed: () {},
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Text("Notice"),
content: Text("Launching this missile will destroy the entire universe. Is this what you intended to do?"),
actions: [
remindButton,
cancelButton,
launchButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
Handling button presses
The onPressed callback for the buttons in the examples above were empty, but you could add something like this:
Widget launchButton = TextButton(
child: Text("Launch missile"),
onPressed: () {
Navigator.of(context).pop(); // dismiss dialog
launchMissile();
},
);
If you make the callback null, then the button will be disabled.
onPressed: null,
Supplemental code
Here is the code for main.dart in case you weren't getting the functions above to run.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Flutter'),
),
body: MyLayout()),
);
}
}
class MyLayout extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
child: Text('Show alert'),
onPressed: () {
showAlertDialog(context);
},
),
);
}
}
// replace this function with the examples above
showAlertDialog(BuildContext context) { ... }
I used similar approach, but I wanted to
Keep the Dialog code as a widget in a separated file so I can reuse it.
Blurr the background when the dialog is shown.
Code:
1. alertDialog_widget.dart
import 'dart:ui';
import 'package:flutter/material.dart';
class BlurryDialog extends StatelessWidget {
String title;
String content;
VoidCallback continueCallBack;
BlurryDialog(this.title, this.content, this.continueCallBack);
TextStyle textStyle = TextStyle (color: Colors.black);
#override
Widget build(BuildContext context) {
return BackdropFilter(
filter: ImageFilter.blur(sigmaX: 6, sigmaY: 6),
child: AlertDialog(
title: new Text(title,style: textStyle,),
content: new Text(content, style: textStyle,),
actions: <Widget>[
new FlatButton(
child: new Text("Continue"),
onPressed: () {
continueCallBack();
},
),
new FlatButton(
child: Text("Cancel"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
));
}
}
You can call this in main (or wherever you want) by creating a new method like:
_showDialog(BuildContext context)
{
VoidCallback continueCallBack = () => {
Navigator.of(context).pop(),
// code on continue comes here
};
BlurryDialog alert = BlurryDialog("Abort","Are you sure you want to abort this operation?",continueCallBack);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
You can use this code snippet for creating a two buttoned Alert box,
import 'package:flutter/material.dart';
class BaseAlertDialog extends StatelessWidget {
//When creating please recheck 'context' if there is an error!
Color _color = Color.fromARGB(220, 117, 218 ,255);
String _title;
String _content;
String _yes;
String _no;
Function _yesOnPressed;
Function _noOnPressed;
BaseAlertDialog({String title, String content, Function yesOnPressed, Function noOnPressed, String yes = "Yes", String no = "No"}){
this._title = title;
this._content = content;
this._yesOnPressed = yesOnPressed;
this._noOnPressed = noOnPressed;
this._yes = yes;
this._no = no;
}
#override
Widget build(BuildContext context) {
return AlertDialog(
title: new Text(this._title),
content: new Text(this._content),
backgroundColor: this._color,
shape:
RoundedRectangleBorder(borderRadius: new BorderRadius.circular(15)),
actions: <Widget>[
new FlatButton(
child: new Text(this._yes),
textColor: Colors.greenAccent,
onPressed: () {
this._yesOnPressed();
},
),
new FlatButton(
child: Text(this._no),
textColor: Colors.redAccent,
onPressed: () {
this._noOnPressed();
},
),
],
);
}
}
To show the dialog you can have a method that calls it NB after importing BaseAlertDialog class
_confirmRegister() {
var baseDialog = BaseAlertDialog(
title: "Confirm Registration",
content: "I Agree that the information provided is correct",
yesOnPressed: () {},
noOnPressed: () {},
yes: "Agree",
no: "Cancel");
showDialog(context: context, builder: (BuildContext context) => baseDialog);
}
OUTPUT WILL BE LIKE THIS
Here is a shorter, but complete code.
If you need a dialog with only one button:
await showDialog(
context: context,
builder: (context) => new AlertDialog(
title: new Text('Message'),
content: Text(
'Your file is saved.'),
actions: <Widget>[
new FlatButton(
onPressed: () {
Navigator.of(context, rootNavigator: true)
.pop(); // dismisses only the dialog and returns nothing
},
child: new Text('OK'),
),
],
),
);
If you need a dialog with Yes/No buttons:
onPressed: () async {
bool result = await showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Confirmation'),
content: Text('Do you want to save?'),
actions: <Widget>[
new FlatButton(
onPressed: () {
Navigator.of(context, rootNavigator: true)
.pop(false); // dismisses only the dialog and returns false
},
child: Text('No'),
),
FlatButton(
onPressed: () {
Navigator.of(context, rootNavigator: true)
.pop(true); // dismisses only the dialog and returns true
},
child: Text('Yes'),
),
],
);
},
);
if (result) {
if (missingvalue) {
Scaffold.of(context).showSnackBar(new SnackBar(
content: new Text('Missing Value'),
));
} else {
saveObject();
Navigator.of(context).pop(_myObject); // dismisses the entire widget
}
} else {
Navigator.of(context).pop(_myObject); // dismisses the entire widget
}
}
Simply used this custom dialog class which field you not needed to leave it or make it null so this customization you got easily.
import 'package:flutter/material.dart';
class CustomAlertDialog extends StatelessWidget {
final Color bgColor;
final String title;
final String message;
final String positiveBtnText;
final String negativeBtnText;
final Function onPostivePressed;
final Function onNegativePressed;
final double circularBorderRadius;
CustomAlertDialog({
this.title,
this.message,
this.circularBorderRadius = 15.0,
this.bgColor = Colors.white,
this.positiveBtnText,
this.negativeBtnText,
this.onPostivePressed,
this.onNegativePressed,
}) : assert(bgColor != null),
assert(circularBorderRadius != null);
#override
Widget build(BuildContext context) {
return AlertDialog(
title: title != null ? Text(title) : null,
content: message != null ? Text(message) : null,
backgroundColor: bgColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(circularBorderRadius)),
actions: <Widget>[
negativeBtnText != null
? FlatButton(
child: Text(negativeBtnText),
textColor: Theme.of(context).accentColor,
onPressed: () {
Navigator.of(context).pop();
if (onNegativePressed != null) {
onNegativePressed();
}
},
)
: null,
positiveBtnText != null
? FlatButton(
child: Text(positiveBtnText),
textColor: Theme.of(context).accentColor,
onPressed: () {
if (onPostivePressed != null) {
onPostivePressed();
}
},
)
: null,
],
);
}
}
Usage:
var dialog = CustomAlertDialog(
title: "Logout",
message: "Are you sure, do you want to logout?",
onPostivePressed: () {},
positiveBtnText: 'Yes',
negativeBtnText: 'No');
showDialog(
context: context,
builder: (BuildContext context) => dialog);
Output:
Or you can use RFlutter Alert library for that. It is easily customizable and easy-to-use. Its default style includes rounded corners and you can add buttons as much as you want.
Basic Alert:
Alert(context: context, title: "RFLUTTER", desc: "Flutter is awesome.").show();
Alert with Button:
Alert(
context: context,
type: AlertType.error,
title: "RFLUTTER ALERT",
desc: "Flutter is more awesome with RFlutter Alert.",
buttons: [
DialogButton(
child: Text(
"COOL",
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: () => Navigator.pop(context),
width: 120,
)
],
).show();
You can also define generic alert styles.
*I'm one of developer of RFlutter Alert.
Minumum code for alert dialog
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text('Title'),
content: Text(
'Content widget',
),
),
);
If you want beautiful and responsive alert dialog then you can use flutter packages like
rflutter alert ,fancy dialog,rich alert,sweet alert dialogs,easy dialog & easy alert
These alerts are good looking and responsive. Among them rflutter alert is the best. currently I am using rflutter alert for my apps.
showAlertDialog(BuildContext context, String message, String heading,
String buttonAcceptTitle, String buttonCancelTitle) {
// set up the buttons
Widget cancelButton = FlatButton(
child: Text(buttonCancelTitle),
onPressed: () {},
);
Widget continueButton = FlatButton(
child: Text(buttonAcceptTitle),
onPressed: () {
},
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Text(heading),
content: Text(message),
actions: [
cancelButton,
continueButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
called like:
showAlertDialog(context, 'Are you sure you want to delete?', "AppName" , "Ok", "Cancel");
Check out Flutter Dropdown Banner to easily alert users of events and prompt action without having to manage the complexity of presenting, delaying, and dismissing the component.
To set it up:
import 'packages:dropdown_banner/dropdown_banner.dart';
...
class MainApp extends StatelessWidget {
...
#override
Widget build(BuildContext context) {
final navigatorKey = GlobalKey<NavigatorState>();
...
return MaterialApp(
...
home: DropdownBanner(
child: Scaffold(...),
navigatorKey: navigatorKey,
),
);
}
}
To use it:
import 'packages:dropdown_banner/dropdown_banner.dart';
...
class SomeClass {
...
void doSomethingThenFail() {
DropdownBanner.showBanner(
text: 'Failed to complete network request',
color: Colors.red,
textStyle: TextStyle(color: Colors.white),
);
}
}
Click here to see an example
Just to add to the great answers - the best package I found is:
adaptive_dialog: ^1.8.0+1
For a one OK button the best thing I found is using showOkAlertDialog
Implementation:
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:flutter/material.dart';
Widget build(BuildContext context) {
return Container(
child: Center(
child: IconButton(
icon: Icon(
Icons.info,
),
onPressed: () => showOkAlertDialog(
context: context,
okLabel: 'OK',
title: 'Title',
message: 'This is the message',
),
)),
);
}
Clean and dismisses when you click 'Ok'.
If you need a dialog so this code for you. just use showDialog() onPress or any inside a function.
void showDialog() {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text("Login Failed!"),
content: const Text(
"Invalid credential !! Please check your email or password",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w400),
),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(ctx).pop();
},
child: Container(
child: const Text(
"Try again",
style: TextStyle(color: Colors.cyan, fontSize: 17),
),
),
),
],
),
)}
Demo dialog screenshots
hope its helpful๐Ÿ˜Š๐Ÿ˜Š๐Ÿ˜Š
Simple and working solution that I used: Enjoy
// Sample can be used for exit dialog box on apps
showAlertDialog(BuildContext context) {
Widget okButton = TextButton(
child: const Text("Leave now",style: TextStyle(color: Colors.red),),
onPressed: () { SystemNavigator.pop(); },
);
Widget nopeButton = TextButton(
child: const Text("Stay here"),
onPressed: () { Navigator.pop(context); },
);
AlertDialog alert = AlertDialog(
title: const Text("Leave"),
content: const Text("Are you sure you want to leave?"),
actions: [
nopeButton,
okButton,
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
Another easy option to show Dialog is to use stacked_services package
_dialogService.showDialog(
title: "Title",
description: "Dialog message Tex",
);
});
This code works and demonstrates how to obtain the button value pressed by the user:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const Center(
child: MyStatelessWidget(),
),
),
);
}
}
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return TextButton(
onPressed: () {
// set up the buttons
Widget cancelButton = TextButton(
child: Text("Cancel"),
onPressed: () => Navigator.pop(context, 'Cancel'),
);
Widget continueButton = TextButton(
child: Text("Ok"),
onPressed: () => Navigator.pop(context, 'Ok'),
);
showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text('AlertDialog Title'),
content: const Text('AlertDialog description'),
actions: <Widget>[
cancelButton,
continueButton,
],
),
).then((value) => print(value));
},
child: const Text('Show Dialog'),
);
}
}
Pressing on Ok button. then on Cancel button print
`showDialog<String>(
context: context,
builder: (BuildContext context) =>
AlertDialog(
title: const Text(
'Invalid Password',
style: TextStyle(color: Colors.red),
),
content:
const Text('Create Strong Password'),
actions: <Widget>[
Center(
child: TextButton(
style: TextButton.styleFrom(
primary: Colors.white,
backgroundColor: Colors
.red, // Background Color
),
onPressed: () => Navigator.pop(
context, 'Cancel'),
child: const Text('Cancel'),
),
),
],
),
),`

Resources