Flutter dialog with MVC - flutter

I am trying to display a dialog box with MVC pattern.
I want the dialog box to be a widget. like so:
AlertDialog gameDecisionDialog({
required VoidCallback onClick,
required String strDecision,
required Color decisionColor,
required BuildContext context,
}) {
return AlertDialog(
titleTextStyle: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 20,
),
actionsOverflowButtonSpacing: 20,
actions: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
return onClick();
},
child: const Icon(Icons.next_plan_outlined),
),
],
content: Text(strDecision),
);
}
This dialog will be called in the model layer. Depending what happens during the app, a dialog will appear. The issue, is the context portion.
does it make sense to pass context from the view layer down to controller layer and then to model layer? Seems inefficient.
Any ideas on how to do this? I am trying to avoid having the dialog box in the view layer, its going to get too messy.
---------------- UPDATE
modified my code to the below suggestion, BUT now my alert dialog doesn't show up.
See the following code (when button clicked do some stuff and then display dialog):
elevatedRectButton(
onClick: () {
setState(() {
MyController.stop();
gameDecisionDialog(
onClick: () {
MyController.start();
},
gameDecision: MyController.getGameDecision,
decisionColor: Colors.amber,
context: context,
);
});
},
mIcon: const Icon(Icons.do_not_touch),
subText: 'STOP',
minWidth: 20,
height: 20,
bgColor: Colors.red,
),
I fear that calling a widget within a widget might be causing this issue?

Passing a BuildContext into a model/controller would not be recommended. Try to call the alert from the widget after the work in the model has been done or when an error is thrown.
Example:
onPress: () async {
//Some trigger callback
startLoading();
await controller.doWork().catchError((e) {
stopLoading();
showAlert(context, 'Some Message etc');
});
stopLoading();
}

Related

Flutter update refresh previous page when page has been pushed via a stateless widget

So here is the problem.
TabScreen() with 3 pages and one fabcontainer button (Stateless widget).
When pressed the fabcontainer will give you the chances of make one upload, after the upload i would like to refresh one of the page of the tabscreen.
return Container(
height: 45.0,
width: 45.0,
// ignore: missing_required_param
child: FabContainer(
icon: Ionicons.add_outline,
mini: true,
),
);
}
OnTap of the fabcontainer:
Navigator.pop(context);
Navigator.of(context).push(
CupertinoPageRoute(
builder: (_) => CreatePost(),
),
);
},
Cannot add a .then(){setState... } because it is a stateless widget and i need to set the state of a precise page, not of the fabcontainer.
Any idea?
Thanks!
Define a updateUi method inside your TabScreen (which defines the pages)
TabScreen:
void updateUi(){
// here your logic to change the ui
// call setState after you made your changes
setState(() => {});
}
Pass this function as a constructor param to your FabContainer button
FabContainer(
icon: Ionicons.add_outline,
mini: true,
callback: updateUi,
),
Define it in your FabContainer class
final Function() callback;
Call it to update the ui
callback.call();
So what Ozan suggested was a very good beginning but i could not access the stateful widget in order to set the state.
What i did on top of Ozan's suggestion was giving the state a globalkey:
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
Assigning it to the scaffold:
return Scaffold(
key: scaffoldKey,
Making the state public removing the _MyPizzasState -> MyPizzasState
Creating a method to refresh the data:
refreshData() {
pizzas = postService.getMyPizzas();
setState(() {
});
}
Assigning a key during the creation of the MyPizzaPage:
final myPizzasKey = GlobalKey<MyPizzasState>();
{
'title': 'My Pizza',
'icon': Ionicons.pizza_sharp,
'page': MyPizzas(key: myPizzasKey),
'index': 0,
},
And, how Ozan said once i received the callback :
buildFab() {
return Container(
height: 45.0,
width: 45.0,
// ignore: missing_required_param
child: FabContainer(
icon: Ionicons.add_outline,
mini: true,
callback: refreshMyPizzas,
),
);
}
void refreshMyPizzas() {
print("Refreshing");
myPizzasKey.currentState?.refreshData();
}

Two stateFullWidget error This widget has been unmounted, so the State no longer has a context

this question similar on flutter error: This widget has been unmounted, so the State no longer has a context (and should be considered defunct) flutter
i have two stateFullWidget
PersonScreen
_HandScreen
where in PersonScreen i have Column
...
PersonScreen
children [
Text('Hand'),
_HandScreen()
]
and then in HandScreen
i have DragTarget
DragTarget<String>(
onAccept: (value) async {
print('value $value');
await onAccept(value);
},
...
onAccept(value) async {
// i tried to open view alert or automatically navigator into any screen
try {
Alert(
context: context,
title: 'Finished!',
desc: 'You\'ve reached ',
image:Lottie.asset(
'assets/lotties/completed-check.json',
height: 85.0,
width: 85.0,
),
buttons: [
DialogButton(
child: Text(
"Selesai",
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: () => Navigator.of(context, rootNavigator: true).pop(),
color: Color.fromRGBO(0, 179, 134, 1.0),
),
],
).show();
} catch(e) {
print('error = $e'); // this throw This widget has been unmounted, so the State no longer has a context (and should be considered defunct)
}
}
but when i tried drag an item against (x2) into accept event, the alert can view or open.
i have change the instead of Alert with Navigator.pushNamed(context, MainScreen.id); the error is same.
i have tried with one stateFullWidget its look normal on running , but how i can handle it with two stateFullWidget , its posible ? because a lot of code in _HandScreen, i dont want to wrap into PersonScreen. by the way i use Provider instead of setState.

Flutter FlushBar does not work if I have to pre-process data

I am trying to build a form in Flutter. A user enters a value and clicks on a button, I run some basic validation on that value then show an AlertDialogue as a confirmation. If the user clicks on confirm I want to attempt an API call, get the result and display the result of that API call to let the user know what happened.If I display a Flushbar with hardcoded values it works. But If I try to do some string manipulation on the object first the Flushbar does not display. If I try to print the response from the function right into the Flushbar that also does not work.
Any advice on why this problem is occurring in the first place, and what would be the best way to solve it?
Padding(
padding: const EdgeInsets.symmetric(vertical: 15.0),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
textStyle: TextStyle(
fontSize: 30,
),
primary: Colors.lightGreen, // background
onPrimary: Colors.white, // foreground
),
onPressed: () {
// Validate returns true if the form is valid, or false otherwise.
if (_formKey.currentState.validate())
{
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text('Confirmation'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('Please confirm your action'),
Text('Would you like to buy ' + finalValue.toString() + ' worth of merchandise'),
],
),
),
actions: <Widget>[
TextButton(
child: Text('No'),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: Text('Confirm'),
onPressed: () {
Navigator.of(context).pop();
var response = bb.trade(_character, finalValue, true); //API CALL
*//String resp = response.body.split(',')[1].split(':')[1];
//resp = resp.substring(1);
//resp = resp.split('.')[0];
//print(resp);* //The real string manipulation we want to do but then flushbar does not display
Flushbar(
title: "Result",
message: "lol"+response.body.toString(), //FLUSHBAR DOES NOT DISPLAY AT ALL
duration: Duration(seconds: 5),
)..show(context);
},
),
],
);
},
);
}
},
child: Text('Buy'),
),
),
I Think the problem is that you are trying to access data from API call immediately , however data coming over API call must be awaited for.
and it is preferred to pop after showing the flush bar .
so if bb.trade(_character, finalValue, true); return future you should do like this
onPressed: () async {
var response = await bb.trade(_character, finalValue, true);
Flushbar(
title: "Result",
message: "lol"+response.body.toString(),
duration: Duration(seconds: 5),
)..show(context).then((value) => Navigator.pop(context));
},

How can I pass an async from page 1 to page 2, and execute the function in page 2 but display results on page 1?

I'm accessing a menu item which redirects me to a second page like so:
void handleClick(String value) {
switch (value) {
case 'Circles':
Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) => new CircleMaker()));
break;
On this second page, I have three TextFormFields as a StatefulWidget. They look like this:
new Container(
decoration: new BoxDecoration(
color: Colors.transparent,
backgroundBlendMode: BlendMode.colorBurn),
child: TextFormField(
controller: longitudeController,
cursorColor: Colors.white,
style: TextStyle(
color: Colors.white,
),
decoration: InputDecoration(
labelText: 'longitude',
))),
Lastly, I have this button on page 2:
FloatingActionButton(
child: Icon(Icons.lens),
backgroundColor: isPressed ? Colors.red : Colors.grey,
onPressed: () {
_makeCircles();
setState(() => isPressed = !isPressed);
},
)])
]),
)]));
}
}
The _makeCircles(); function is not defined/available in the page 2 build. But it's defined/available on page 1 and works perfectly. This is the function:
Set<Circle> circles;
Future<void> _makeCircles() async {
setState(() {
//work
]);
});
}
How can I grab this async function which is available on page 1, use it on page 2, and then be sent back to page 1 (which is a google map). To say this in another way, I want to enter my TextFormFields on page 2 and use them to plot a circle on page 1. Anybody know how to do this?
Create a function sendData in page2
sendData() async {
//Code to get location using Geolocator package.
return position;
}
In your page1,
Access sendData() function of page2 inside page1, to get your data.

Tooltip onTap rather than onLongPress possible?

there's no named parameters to configure a single tap to trigger Tooltip,
my feeling about the default longPress interaction is that users cannot find this deep-buried function.
I tried to find some hint in tooltip source code but failed.
Tooltip(
message: 'this is something',
child: SizedBox(...),
)
First, define globalkey: GlobalKey _toolTipKey = GlobalKey();
Then wrap your tooltip:
GestureDetector(
onTap: () {
final dynamic _toolTip = _toolTipKey.currentState;
_toolTip.ensureTooltipVisible();
},
child: Tooltip(
key: _toolTipKey,
message: "Your message",
child: Icon(
Icons.info,
),
),
),
Easiest way is to use:
triggerMode: TooltipTriggerMode.tap
Here's an example:
Tooltip(
triggerMode: TooltipTriggerMode.tap,
message: 'this is something',
child: SizedBox(...),
)
The easiest way to get a functionality you need is to clone the original Tooltip widget (call it e.g. TooltipCustom) and change inner GestureDetector behavior.
Particularly replace onLongPress to onTap:
class TooltipCustom extends StatefulWidget {
/// Creates a tooltip.
...
#override
Widget build(BuildContext context) {
...
Widget result = GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: _handleLongPress,
excludeFromSemantics: true,
child: Semantics(
label: excludeFromSemantics ? null : widget.message,
child: widget.child,
),
);
...
return result;
}
}
P.S. It's possible to lose a tooltip hiding feature. Take a look at _handlePointerEvent(PointerEvent event) handler function and realize a proper call of _hideTooltip() method.