How can I use showDialog with await - async-await

I have three examples of showDialog. I presume that _showAlert1 is correct, however it uses 2 functions to achieve it. _showAlert2 also works, however I presume it is not correct because I believe that showDialog is async, and I presume that this function relies on the dialog being displayed in sufficient time. _showAlert3 does not work because the dialog stays on the screen and does not clear.
If _showAlert2 although it works is incorrect for the above reason, could someone please show me how this should be structured so that this can be done in one function.
Examples:
void _showAlert0(BuildContext context, String text, int seconds) async {
return await showDialog(
barrierDismissible: false,
context: context,
builder: (context) => AlertDialog(
title: Text("Error"),
content: Text(text),
));
}
void _showAlert1(BuildContext context, String text, int seconds) async {
_showAlert0(context, text, seconds);
await Future.delayed(Duration(seconds: seconds));
Navigator.of(context).pop(true);
}
void _showAlert2(BuildContext context, String text, int seconds) async {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) => AlertDialog(
title: Text("Error"),
content: Text(text),
));
await Future.delayed(Duration(seconds: seconds));
Navigator.of(context).pop(true);
}
void _showAlert3(BuildContext context, String text, int seconds) async {
await showDialog(
barrierDismissible: false,
context: context,
builder: (context) => AlertDialog(
title: Text("Error"),
content: Text(text),
));
await Future.delayed(Duration(seconds: seconds));
Navigator.of(context).pop(true);
}

Not sure if there is a better way, but the following appears to work. Note the "then" clause on the call to showDialog().
void _showAlert3(BuildContext context, String text, int seconds) async {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) => AlertDialog(
title: Text("Error"),
content: Text(text),
)).then((val) {});
await Future.delayed(Duration(seconds: seconds));
Navigator.of(context).pop(true);
}
As for trolls, RTFQ ("structured so that this can be done in one function") and if you don't want to help then go away.

Related

What is the best way to send data using an API to a springboot server using flutter?

I'm working on a flutter mobile app and I'm wondering about the cleanest way to send data to a springboot server.
Here we do use flutter http package to obtain/ work with data from apis.
Here's an example
import 'package:flutter/material.dart';
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Welcome to My App",
home: AppBody(),
);
}
}
class AppBody extends StatefulWidget {
#override
_AppBodyState createState() => _AppBodyState();
}
class _AppBodyState extends State<AppBody> {
final apiURL = "localhost:8080";
final path = "/api/posts/read?search=shyam&start=1&limit=100";
late var url;
#override
void initState() {
// TODO: implement initState
super.initState();
// url = Uri.http(apiURL, path);
// url = Uri.http(apiURL+path);
url = Uri.http(amazonUri, path);
getData();
}
Future<void> getData() async {
var response = await http.get(url);
if (response.statusCode == 200) {
print("Data obtained successfully");
print(response.body);
} else {
print("Issues with APIs");
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: Icon(Icons.agriculture),
title: Text("App Bar"),
),
body: Container(
child: Center(
child: Text("Hello world"),
),
),
);
}
}
**you can use dio packge for any kind of apis its easy and best and you do not need any kind maping it will be by dio package own its own **
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:dio/dio.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:rent_house/screens/Navigation/navBar.dart';
Future<String> signupApis({
name,
email,
conatact,
address,
password,
type,
context,
}) async {
// isloading:true;
var apiURL = 'https://denga.r3therapeutic.com/public/api/register';
var formData = FormData.fromMap({
'name': name,
'email': email,
'contact': conatact,
'address': address,
'password': password,
'type': type,
});
//final prefs = await SharedPreferences.getInstance();
Dio dio = Dio();
Response responce;
try {
responce = await dio.post(
apiURL,
data: formData,
);
// print("response datra " + responce.toString());
SharedPreferences pref = await SharedPreferences.getInstance();
var res1 = responce.data['user'];
var token = res1['token'];
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Navbar()),
);
Fluttertoast.showToast(
msg: "Login Successfull", backgroundColor: Colors.cyan);
return '';
} catch (e) {
Fluttertoast.showToast(
msg: "User Already exists", backgroundColor: Colors.cyan);
return 'some thing wrong';
}
}
I don't know whether it will be the cleanest way or not but you can use REST APIs to get/send data from/to Springboot Server.
For more information: https://medium.com/nerd-for-tech/flutter-series-connecting-ui-to-spring-boot-backend-f9874dc3dcd5
For your Flutter Project structure, you can use BLoC or any other state management.
You can use spring as RestAPI which has logic something like http method(get, post, put, delete..).
(RestfulAPI is used many reason, one of reason is for various client.(mobile, web, pad,..))
You can send data from client to server on query string in uri, header or body.

LogIn screen skipped on flutter

Currently I am designing my UI for the login screen and Firebase authentication is already in effect as I can see the username on the firebase console, however I try to edit my UI for the login screen, but the login screen is skipped and is continuously logged in, so I cannot see the changes I am making for the UI of my login screen, here is my code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'home.dart';
class Login extends StatefulWidget {
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
final GoogleSignIn googleSignIn = new GoogleSignIn();
final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
SharedPreferences preferences;
bool loading = false;
bool isLogedin = false;
#override
void initState() {
super.initState();
isSignedIn();
}
void isSignedIn() async {
setState(() {
loading = true;
});
preferences = await SharedPreferences.getInstance();
isLogedin = await googleSignIn.isSignedIn();
if (isLogedin) {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => HomePage()));
}
setState(() {
loading = false;
});
}
Future handleSignIn() async {
preferences = await SharedPreferences.getInstance();
setState(() {
loading = true;
});
GoogleSignInAccount googleUser = await googleSignIn.signIn();
GoogleSignInAuthentication googleSignInAuthentication =
await googleUser.authentication;
AuthCredential credential = GoogleAuthProvider.getCredential(
idToken: googleSignInAuthentication.idToken,
accessToken: googleSignInAuthentication.accessToken);
FirebaseUser firebaseUser =
(await firebaseAuth.signInWithCredential(credential)).user;
if (firebaseUser != null) {
final QuerySnapshot result = await Firestore.instance
.collection("users")
.where("id", isEqualTo: firebaseUser.uid)
.getDocuments();
final List<DocumentSnapshot> documents = result.documents;
if (documents.length == 0) {
//insert the user to our collection
Firestore.instance
.collection("users")
.document(firebaseUser.uid)
.setData({
"id": firebaseUser.uid,
"username": firebaseUser.displayName,
"profilePicture": firebaseUser.photoUrl
});
await preferences.setString("id", firebaseUser.uid);
await preferences.setString("username", firebaseUser.displayName);
await preferences.setString("photUrl", firebaseUser.displayName);
} else {
await preferences.setString("id", documents[0]['id']);
await preferences.setString("username", documents[0]['username']);
await preferences.setString("photoUrl", documents[0]['photoUrl']);
}
Fluttertoast.showToast(msg: "Login was successful");
setState(() {
loading = false;
});
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => HomePage()));
} else {
Fluttertoast.showToast(msg: "Login failed :(");
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
centerTitle: true,
title: new Text(
"Login",
style: TextStyle(color: Colors.red.shade900),
),
elevation: 0.1,
),
body: Stack(children: <Widget>[
Visibility(
visible: loading ?? true,
child: Center(
child: Container(
alignment: Alignment.center,
color: Colors.white.withOpacity(0.9),
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.red),
))))
,
]),
bottomNavigationBar: Container(
child: Padding(
padding: const EdgeInsets.only(left:12.0, right: 12, top: 12, bottom: 12),
child: FlatButton(
color: Colors.red.shade900,
onPressed: () {
handleSignIn();
},
child: Text(
'Sign in/ Sign up with Google',
style: TextStyle(color: Colors.white),
)),
),
),
);
}
}
This is my code for the UI of login, however I cannot see the edits I am making on this on my emulator, as the login screen just skips (actually it appears for like 0.5 seconds and goes directly to the homepage). Could someone help me out?
You are navigating the logged-in user to the home page.
void isSignedIn() async {
setState(() {
loading = true;
});
preferences = await SharedPreferences.getInstance();
isLogedin = await googleSignIn.isSignedIn();
if (isLogedin) {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => HomePage()));
}
setState(() {
loading = false;
});
}
Either you can implement Logout in the app or just comment out the auto logged in part until your testing finish.

Flutter: bloc, how to show an alert dialog

I´m new in the bloc pattern and stream stuff. I want to show up an alert dialog when I press a button, but I can´t find a way to do it. Actually my code is:
Widget button() {
return RaisedButton(
child: Text('Show alert'),
color: Colors.blue[700],
textColor: Colors.white,
onPressed: () {
bloc.submit();
});
}
return Scaffold(
appBar: AppBar(
title: Text("Title"),
),
body: StreamBuilder(
stream: bloc.getAlert,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text("I have Dataaaaaa ${snapshot.data}");
} else
return ListView(
children: <Widget>[
Container(
button()
)
...
And the BLoC:
final _submissionController = StreamController();
Stream get submissionStream=> _submissionController.stream;
Sink get submissionSink=> _submissionController.sink;
I tried to do something like:
Widget button() {
return StreamBuilder(
stream: submissionStream
builder: (context, snapshot){
if (snapshot.hasData){
return showDialog(...)
}else
return RaisedButton(
child: Text('Show alert'),
color: Colors.blue[700],
textColor: Colors.white,
onPressed: () {
bloc.submit();
});
}
But, of course, it didn´t work.
You can't show a dialog when build working. When you have new data, then you create a new widget. Probably better for you will be not using the stream in this case, but if it necessary you should use
WidgetsBinding.instance.addPostFrameCallback((_) => yourFunction(context));
or
Future.microtask(() => showDialogFunction(context));
in your if
if (snapshot.hasData) {
WidgetsBinding.instance.addPostFrameCallback((_) => showDialogFunction(context));
}
This code will be launched after build method, so dialog will show immediately.
Bloc function always return widget, so always return button() or different wiget when stream has data
You can use BlocListener for showing Dialogs, Snackbars or for navigating to a new page.
With this approach you may want to refactor to rely on the bloc state rather than accessing the stream directly.
Listener is guaranteed to only be called once for each state change, however builder can be called many times. Also you can't do some operations on builders, such as navigating to another page.
return Scaffold(
appBar: AppBar(
title: Text("Title"),
),
body: BlocProvider<YourBloc>(
create: () => YourBloc(),
child: Stack([
SnackbarManager(),
YourScreen(),
]),
),
);
...
/// This is basically an empty UI widget that only
/// manages the snackbar
class SnackbarManager extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocListener<YourBloc, YourBlocState>(
listener: (context, state) {
if (state.hasMyData) {
Scaffold.of(context).showSnackBar(SnackBar(
content:
Text("I got data"),
));
}
},
child: Container(),
);
}
}
I know I'm late to the party, but maybe this will help someone.
I'm currently learning about BLoC myself and ran into a similar problem.
First of all, I want to recommend the flutter_bloc package from pub.dev.
It contains Widgets that help you with this like BlocListener and BlocConsumer.
If you want to go without it, you could try using a StatefulWidget and listen to it separately and use your logic to show the dialog. (also make sure your stream is broadcasting as in my example, so it can have multiple listeners)
I've made an example which you could copy-past into dartpad.dev/flutter:
import 'dart:async';
import 'package:flutter/material.dart';
final myStream = StreamController<bool>.broadcast();
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark(),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
initState() {
super.initState();
myStream.stream.listen((show){
if(show)
showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
return AlertDialog(
title: Text('MyDialog'),
actions: [
TextButton(
child: Text('Close'),
onPressed: (){
myStream.sink.add(false);
}),
]
);
}
);
if(!show) {
Navigator.pop(context);
}
});
}
#override
Widget build(BuildContext context) {
return Center(child: ElevatedButton(
child: Text('Show Alert'),
onPressed: (){
myStream.sink.add(true);
}));
}
}
Here is what I did, it might be wrong as I'm also new to flutter. But works for my scenario.
Widget build(BuildContext context) {
final authBloc = BlocProvider.of<AuthBloc>(context);
authBloc.outServerResponse.listen((serverResponse) {
if (serverResponse.status == 'success') {
_navigateToLogin();
} else {
_showSnakBar(serverResponse.message);
}
});
.... Rest of the code which returns the widget,
which in my case is form widget with button for submitting as follows,
onPressed: () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
authBloc.processRegister.add(_registrationData.toMap());
}
}
outServerResponse is the stream that outputs after finishing API POST call.
authBloc.processRegister is the input sink to pass form data to my Auth API Service Provider.
_nagivateToLogin & _showSnakBar are simple functions
_navigateToLogin() {
Navigator.of(context).pop();
}
_showSnakBar(String msg) {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(msg),
),
);
}
this process working for me.
I called my Dialog before return the widget
Future.microtask(() => showLoginSuccess(BuildContext context));
If you're using flutter_bloc package which I suggest to use, you should use the provided BlocListener widget which listens to state changes and could execute logic codes. like this for example:
BlocListener<BlocA, BlocAState>(
listener: (context, state) {
// do stuff here based on BlocA's state
},
child: Container(),
);
but if you also need the build widget, you should use BlocConsumer widget which has the listener and the builder at the same time:
BlocConsumer<BlocA, BlocAState>(
listener: (context, state) {
// do stuff here based on BlocA's state
},
builder: (context, state) {
// return widget here based on BlocA's state
}
);
It's common to show a dialog without changing the build widget,
so BlocConsumer offers the buildWhen option for this situation which takes the previous and current states to decide about the builder:
buildWhen: (state, currentState){
if (state is MainComplexTableState && currentState is NewComplexRegistration) {
return false;
}
if (state is ErrorToShowUp) {
return false;
}
return true;
},
I solved it by maintaining two context as follows
**
BlocProvider of type A ==>widget class B(showdialog(context:context,builder(context2){
Blocprvider.value(value:Blocprovider.of<A>.context)
child:BlocListener(
listner(context2,state)
{//
your works
//}
child:AlertDialog( some widgets
a button function ()=> context.read<A>().function or property name
//
1.here we call old context in fact it is registered with provider, 2. context2 is only for building a new builder widget.
3.hence we get bloc passed through a navigation and accessible in navigated alert widget without creating it

Flutter: How can I prevent default behaviour on key press?

I'm trying to intercept when a user presses the volume buttons to perform a specific action and prevent the default behaviour (volume changes).
This is the code I have so far:
RawKeyboard.instance.addListener(_keyboardListener);
void _keyboardListener(RawKeyEvent e) {
if(e.runtimeType == RawKeyUpEvent) {
RawKeyEventDataAndroid eA = e.data;
if(eA.keyCode == 24) { //volume up key
_goNextPage();
}
if(eA.keyCode == 25) { //volume down key
_goPrevPage();
}
}
}
How would I go about preventing the volume from changing (and stopping the volume slider from appearing at the top)?
A Javascript analogous would be calling event.preventDefault() on the key event.
This seems to be a rather trivial matter, but I haven't been able to find any answers in the docs.
Thanks.
I've faced a similar problem and what to share how I solved it.
To stop the propagation we have to return true from onKey method of a FocusNode in the focus nodes tree. To achieve this I've wrapped my app body with FocusScope and Focus widgets like this:
MaterialApp(
home: Scaffold(
body: FocusScope(
autofocus: true,
child: Focus(
autofocus: true,
canRequestFocus: true,
onKey: (data, event) {
if (event.isKeyPressed(LogicalKeyboardKey.audioVolumeUp)) {
print("Volume up");
return true;
}
if (event
.isKeyPressed(LogicalKeyboardKey.audioVolumeDown)) {
print("Volume down");
return true;
}
return false;
},
child: Text(text: "Hallochen")))))
Thanks to Sergey's answer I was able to solve the issue as well. In my case, I wanted to create a ListView, with pull to refresh (RefreshIndicator) that will work for both mobile devices and web.
I tried to implement a refresh indicator which will appear when the user clicks F5 to refresh the web page, but I had to prevent the browser from actually refreshing the page.
Here's an example of my implementation, which prevents refresh from occuring when the user clicks F5.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class ExamplePage extends StatefulWidget {
#override
_ExamplePageState createState() => _ExamplePageState();
}
class _ExamplePageState extends State<ExamplePage> {
final GlobalKey<AnimatedListState> listKey = GlobalKey<AnimatedListState>();
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey = new GlobalKey<RefreshIndicatorState>();
List items = [];
Future<void> _pullRefresh() async {
await Future.delayed(Duration(milliseconds: 1000));
}
#override
Widget build(BuildContext context) {
return FocusScope(
autofocus: true,
child: Focus(
autofocus: true,
canRequestFocus: true,
onKey: (data, event) {
if (event
.isKeyPressed(LogicalKeyboardKey.f5)) {
_refreshIndicatorKey.currentState!.show();
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
},
child: Container(
padding: EdgeInsets.all(15.0),
child: RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: _pullRefresh,
child: AnimatedList(
key: listKey,
initialItemCount: items.length,
itemBuilder: (context, index, animation) {
return _buildItem(context, index, animation);
},
),
),
),
),
);
}
Widget _buildItem(
BuildContext context, int index, Animation<double> animation) {
return Text("Example");
}
}
all the solutions here are for Focus widget, they inspired me but were not quite the solution for me as I was using RawKeyboardListener and did not wanna change to something else.
here is what worked for me:
final node = FocusNode();
final fnode = FocusScopeNode();
#override
Widget build(BuildContext context) {
return FocusScope(
node: fnode,
child: RawKeyboardListener(
focusNode: node,
...
),
);
}

Use progress bar dialog in async task using Future in Flutter

I want to create common class for execute API. Now i needed to add progress dialog while executing task and after complete task dialog should be dismiss.I google lot but not get appropriate solution So help me to achieve it.
For Http Client i used dio plugin.
Please help me for adding progress dialog in this class so when i create request using this class it added progress dialog while executing task. i create this type of class in java but now i want to add it in flutter.
HttpRequest.dart
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
class HttpRequest {
void post(BuildContext context,String url, dynamic data, HttpListener listener) {
Dio dio = new Dio();
dio.post(url, data: data)
.then((Response<dynamic> response) {
if (response.statusCode == 200) {
listener.onSuccess(response.data);
} else {
listener.onError("Error");
}
})
.catchError((Exception error) {
listener.onError(error.toString());
});
}
}
abstract class HttpListener {
void onSuccess(dynamic result);
void onError(String error);
}
It's better to show progressbar in widgets, not in common classes.
Use below example (using http package):
class HttpRequest {
final JsonDecoder _decoder = new JsonDecoder();
Future post(String url, dynamic data) async {
http.Response response = await http.post(url,body: data);
if(response.statusCode < 200 || response.statusCode > 300){
throw new Exception('Faild');
} else {
return _decoder.convert(response.body);
}
}
}
The button that calls post method:
child: MaterialButton(onPressed: () async {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}));
HttpRequest _util = new HttpRequest();
try{
var res = await _util.post('someurl',_data);
} catch(Exception) {
//Handle Exception
} finally {
Navigator.pop(context);
}
});
I made a public package, future_progress_dialog, which is the opposite concept of package.
You wanted to put progress dialog in the task. But I tried the opposite way, putting Future task into the dialog.
https://pub.dev/packages/future_progress_dialog
With this package you can make a progress dialog like this.
var result = await showDialog(
context: context,
child: FutureProgressDialog(future, 'Loading...'));
I hope this would be helpful.
It is better to do it in fully async mode
onPressed: () async {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}));
authenticate().then((value) {
Navigator.pop(context);
Navigator.push(context, MaterialPageRoute(builder: (context) => HomePage()));
}).onError((error, stackTrace) {
Navigator.pop(context);
_showErrorToast(context);
});
},

Resources