How distinguish inner context? - flutter

I wrote the code like data: (data){Navigator.of(context).pop();},//here to pop showDialog but actually other Navigator.of(context) was popped and figured out that I should distinguish inner context in builder: (context) { //here2 from outer context but could not reach how to do that. Would somebody please give me some advise to solve this ?
final authC = ref.watch(authFutureProvider);
authC.when(
data: (data){Navigator.of(context).pop();},//here
error: (error, stackTrace) {},
loading: () {
final loading =
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) { //here2
return const Center(
child: CircularProgressIndicator(),
);
}
);
});
},
);

Well, you can easily define different BuildContexts by giving them different names, like this:
#override
Widget build(BuildContext parentContext) {
final authC = ref.watch(authFutureProvider);
authC.when(
data: (data){Navigator.of(parentContext).pop();},//here
error: (error, stackTrace) {},
loading: () {
final loading =
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(
barrierDismissible: false,
context: parentContext,
builder: (context) { //here2
return const Center(
child: CircularProgressIndicator(),
);
}
);
});
},
);
return Widget();
}
However, that won't change the fact that the context I've named "parentContext" is the parent context! This is the context sent TO the Dialog, not the context of the Dialog itself.
If you want to pop whichever context is on top at the moment, you can define a GlobalKey variable like this:
//global.dart
import 'package:flutter/material.dart';
class GlobalVariable {
static final GlobalKey<NavigatorState> navState = GlobalKey<NavigatorState>();
}
Then, you can pop the current context like this, from anywhere:
import 'global.dart';
void func(){
Navigator.pop(GlobalVariable.navState.currentContext!);
}
That should pop the Dialog IF the Dialog is currently on top...
But of course, if some other BuildContext is on top, then that will be popped, instead. ๐Ÿ˜ So make sure you write the proper conditions for that.
Edit:
You could also use popUntil(). If this happens to be the first screen, you can just write:
#override
Widget build(BuildContext parentContext) {
final authC = ref.watch(authFutureProvider);
authC.when(
data: (data){Navigator.popUntil(context, (route) => route.isFirst);},//here
error: (error, stackTrace) {},
loading: () {
final loading =
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(
barrierDismissible: false,
context: parentContext,
builder: (context) { //here2
return const Center(
child: CircularProgressIndicator(),
);
}
);
});
},
);
return Widget();
}
If not, then it's a bit more complicated to make it pop back to this particular screen... But you could use routeSettings, for example.

I strongly not recommend WidgetsBinding.instance.addPostFrameCallback to show loading because it takes time to render, so your app will load a loading function. It will make End-user can do multiple clicks on the button because the loading has not rendered yet.
this package is verylight and easy to use, the dismiss and show even different from regular widget, so you will not accidentally close your screen because wrong logic or back-end handler failure.
class LoadingExample {
showLoadingExample(){
EasyLoading.instance
..displayDuration = const Duration(milliseconds: 2000)
..indicatorType = EasyLoadingIndicatorType.fadingCircle
..loadingStyle = EasyLoadingStyle.dark
..indicatorSize = 45.0
..radius = 10.0
..progressColor = Colors.yellow
..backgroundColor = Colors.green
..indicatorColor = Colors.yellow
..textColor = Colors.yellow
..maskColor = Colors.blue.withOpacity(0.5)
..userInteractions = true
..dismissOnTap = false
..customAnimation = CustomAnimation();
}
dismissExampleLoading(){
EasyLoading.dismiss();
}
}
to call it simply do: *I recommend OOP so you can modify it in 1 place when you need.
LoadingExample().showLoadingExample();
or
LoadingExample().dismissExampleLoading();
even you accidentally call show or dissmiss multiple time, the loading will not stack, and it will just recall or reset the animation. So your app will not crash
Flutter_easyloading

Related

ChangeNotifier inaccessible in grandchildren widget of where it was provided

I am trying to use flutter provider in order to carry my state down a widget sub-tree/route, and while it works for the direct child of the widget that provided the change notifier class, it does not for the next one in line.
As far as I understand, the change notifier class should be passed down. To be more specific, I am trying to access it through context.read() in a function being called in its initState function.
Am I doing something wrong?
The code below illustrates my code.
Where it class notifier is provided:
onTap: () {
// Select body area
context.read<Patient>().selectBodyArea(areas[index]);
// Open complaint list
FlowRepresentation flow = context.read<Patient>().getFlow();
Navigator.push(
context,
MaterialPageRoute(builder: (context) =>
ChangeNotifierProvider.value(
value: flow,
child: const ChiefComplaintList()
)
)
);
}
Navigation to the problem widget in ChiefComplaintList:
onTap: () {
// Select complaint
context.read<FlowRepresentation>().selectComplaint(ccs[index]);
// Show factors
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const AttenuationFactors())
);
}
Where I'm having trouble accessing the change notifier class:
void getData() async {
_nrFactors = await context.read<FlowRepresentation>().getAttenuationFactors();
setState(() {}); // rebuild widget with data
}
#override
void initState() {
super.initState();
print("Initiated Attenuation Factors Lists State");
getData();
}

how to open a url with url_launcher and onTap of InkWell only once?

if i touch it once then it prints out 1 line 123
if i touch it many times then it prints out many line 123
So how when I touch it many times then it prints out 1 line 123 or exiting _launchUrl
When I touch it many times then I also had to go back to that number of times to get rid of _launchUrl
My code here
Hope to get everyone's help!
final Uri _url = Uri.parse('https://flutter.dev');
....
Future<void> _launchUrl() async {
if (!await launchUrl(_url)) {
throw 'Could not launch $_url';
}
}
...
InkWell(
onTap: () {
_launchUrl;
print('123');
}
)
I tried using the wait function but still not getting the desired result
Create a variable buttonPressed and set it default to false
bool buttonPressed = false;
Inside your onTap you can check if the buttonPressed is set to false. If it is set to false you can set it to true and run your _launchUrl function. After you called _launchUrl you can set it back to false to run it again.
if(buttonPressed == false){
buttonPressed = true;
await _launchUrl();
buttonPressed = false;
}
Also mark your onTap as async to use the await keyword
onTap: () async {
I fixed it by following way
Uri uri = Uri.parse('https://flutter.dev');
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => LoadURL(uri),
)
);
// Navigate to here
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
class LoadURL extends StatelessWidget {
late final Uri uri;
LoadURL(this.uri);
Future<void> _launchUrl(Uri uri) async {
if (!await launchUrl(uri)) {
throw 'Could not launch $uri';
}
}
#override
Widget build(BuildContext context) {
return Center(
child: FutureBuilder(
future: _launchUrl(uri),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Navigator.pop(context);
}
return CircularProgressIndicator();;
},
),
);
}
}

How to call a method when navigating back from another screen in flutter?

I have a list view of items. For each item in the list, there is an edit option, when on click that im navigating from ListScreen to the EditScreen. Edit Screen has a static update button but the editing view is changed according to the item selected to be edited From the ListScreen.
Now when I update the items in Edit screen, and press update button I can navigate to ListScreen back but I need the list view to rebuild on that. The function to build the list is given inside of a FutureBuilder in ListScreen it self. In the initial navigate to the ListScreen im calling that method in initState().
Is there a way that I can manage this?
ListScreen
//provider
late AddItemProvider? _updateItemProvider;
#override
void initState() {
_dataFuture = _getAddedData().whenComplete(() => refresh());
super.initState();
}
void _gotoEditAccess(BuildContext ctx, String title) async {
var nav =
Navigator.pushNamed(ctx, suggestionUpdateUIRoute, arguments: title);
// of(context)
if (nav != null && nav == true) {
await _dataFuture; //_getAddedData().whenComplete(() => refresh());
}
}
//in the body I have this list view
//added item is the list view item which holds Function parameters for onEdit and onDelete
//used as onTap: () => widget.onEdit!(),
ListView.builder(
shrinkWrap: true,
itemCount: _listOfItems.length,
itemBuilder: (context, index) {
return AddedItem(
icon: _listOfItems[index].icon,
title: _listOfItems[index].title,
content: _listOfItems[index].content,
onEdit: () async {
_gotoEditAccess(
context, _listOfItems[index].title!);
},
onDelete: () {
_deleteItem(_listOfItems[index].title!);
},
);
},
),
edit screen have a base view with common update button and the body is being replaced with a actual editing view matching what needs to be edited.
EditScreen
#override
Widget build(BuildContext context) {
_updateItemProvider = Provider.of<AddItemProvider>(context, listen: false);
...
//rest of the body for matched editing screen...
//inside the base view of edit screen
_goBack() {
Navigator.pop(context, true);}
#override
Widget build(BuildContext context) {
_updateItemProvider = Provider.of<AddItemProvider>(context, listen: false);
_submitBtn() => Consumer<AddItemProvider>(
builder: (_, AddItemProvider updateItemProvider, __) {
_updateItemProvider = updateItemProvider;
return ButtonWidget(
btnColor: CustomColors.green600,
borderColor: CustomColors.green600,
textColor: CustomColors.mWhite,
text: "Update",
eButtonType: eButtonType.bText,
eButtonState: _submitBtnState,
onPressed: () async {
await saveSelectedList(updateItemProvider.updateItems!);
_updateItemProvider!.removeAll();
_goBack();
},
);
},
);
You need to write this code were you want to navigate
onTap:() async{
var test = await Navigator.of(context).push(
MaterialPageRoute(builder: (context) =>EditProfile()));
if(test != null || test == true){
// perform your function
}
}
You need to pass any content when navigate back from edit screen
Navigator.pop(context, true);

The argument type 'String' can't be assigned to the parameter type 'Route<Object?>'.dart(argument_type_not_assignable)

I am trying to get the name of the screen to navigate to as a string(in a custom class), when clicking a button.
class myButton extends StatelessWidget {
String button_txt = '';
String nextPage = '';
double height_ = 39;
myButton(button_txt) {
this.button_txt = button_txt;
this.nextPage = nextPage;
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.pushReplacement(context, '/$nextPage');
},
If you want to use named routes you should use it like this:
Navigator.pushReplacementNamed(context, 'nextPage');
but if you don't want to use named routes there is different way to navigate:
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) {
return routewidget;
},
),
);
There are two ways to do this:
use Navigator.pushNamedReplacement(context, '/$nextPage')
use Navigator.pushReplacement(context, route); with a Route Parameter
If you want to use named routes, then use
Navigator.pushNamedReplacement(context, '/name/of/route).
If it still doesn't work, then you might not have set up navigation properly.
If you want to use non-named routes, then the approach will be different:
Navigator.pushReplacement(
context,
MaterialPageRoute( // Material page route makes it
// slide from the bottom to the top
//
// If you want it to slide from the right to the left, use
// `CupertinoPageRoute()` from the cupertino library.
//
// If you want something else, then create your own route
// https://flutter.dev/docs/cookbook/animation/page-route-animation
builder: (context) {
return NextPageWidget();
},
),
);

Flutter using changeNotifier does not change the appearance of the widget

I have a form on my page.
When I fill out the form, if it is valid a loading pop up appears and I start to perform calculations. Depending on the state of the calculations, the message on the loading popup will change.
I'm trying to do a function in my provider which will change the message depending on the state of the popup.
When I print the values โ€‹โ€‹they are displayed correctly but my message does not change in the widget
How I call my screen :
return MultiProvider(
providers: [
ChangeNotifierProvider<FormCalculatorNotifier>(
create: (BuildContext context) => FormCalculatorNotifier()
),
ChangeNotifierProvider<LoaderNotifier>(
create: (BuildContext context) => LoaderNotifier()
),
],
child: CalculatorScreen(),
);
In my screen, how I call the popup and calculations:
Expanded(
child: ButtonComponent.primary(
context: context,
text: AppTextButton.CALCUL,
onPressed: () async {
await _formProvider.submitForm(context);
if(_formProvider.state == FormProviderState.isSuccess){
// --------------------------------------
// Here I call the popup with the message variable
DialogComponent.loadingPopUp(context: context, description: _loaderProvider.message);
// --------------------------------------
// Here I try to change the popup message because I have my calculations
_loaderProvider.state = LoaderState.isLoadingJsons;
await _loaderProvider.updateMessage();
}
},
),
),
The popUp notifier:
class LoaderNotifier with ChangeNotifier {
LoaderState state = LoaderState.isReady;
String message = "Start...";
Future<void> updateMessage() async{
print('update message');
if(state == LoaderState.isLoadingJsons){
message = "Loading files...";
}
notifyListeners();
}
}
EDIT: I just saw that my problem is that I call the updateMessage function after calling it from the DialogComponent
If I want to follow my logic which is to study if the form is good to display the popup then perform the calculations and according to the calculations change the message of the popup, what to do since I will call the updateMessage function afterwards?