How to call function from Stateful widget - flutter

I have seen many questions similar to mine in Stack Overflow but it did not fit my case since they were asking to call function from - to Stateful widget.
I want call function located into State Full widget from a non Stateful-Stateless Widget
My code is complicated, I will try to explain it below:
class Example extends StatefulWidget {
const Example({Key? key}) : super(key: key);
#override
_ExampleState createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
void myFunction(){
print('hello dart');
}
ShowDialog showDialog = ShowDialog();
#override
Widget build(BuildContext context) {
return TextButton(
onPressed: (){
showDialog.myDialog();
},
child: Text('tab me')
);
}
}
class ShowDialog {
Widget myDialog(){
return showDialog(
builder: (BuildContext context) {
return SimpleDialog(
backgroundColor: Colors.deepPurple[900],
titleTextStyle: const TextStyle(
color: Colors.red, fontSize: 18),
children: [
ElevatedButton(
onPressed: () {
// here i need to call myFunction() Methood
},
child: const Text("tab")
),
],
)
},
);
}
}
How can I go through this?

you can call it directly like this:
_ExampleState().myFunction();
The full code:
class Example extends StatefulWidget {
const Example ({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<Example > createState() => _ExampleState ();
}
class _ExampleState extends State<Example > {
void myFunction(){
print('hello dart');
}
ShowDialog showDialog = ShowDialog();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: TextButton(
onPressed: (){
showDialog.myDialog(context);
},
child: Text('tab me')
)
);
}
}
class ShowDialog {
Future myDialog(BuildContext context){
return showDialog(
context: context,
builder: (BuildContext context) {
return SimpleDialog(
backgroundColor: Colors.deepPurple[900],
titleTextStyle: const TextStyle(
color: Colors.red, fontSize: 18),
children: [
ElevatedButton(
onPressed: () {
// here i need to call myFunction() Methood
_ExampleState().myFunction();
},
child: const Text("tab")
),
],
);
},
);
}
}
The result:

So in order to have your showDialog function call MyFunction, you need to pass it as if it was a callback.
To do that, first add a Function callback in your class:
class ShowDialog {
ShowDialog({required this.callback});
VoidCallback callback;
...
}
Then you have to pass the callback when you create the object:
ShowDialog showDialog = ShowDialog(callback: myFunction);
You actually can't do that tho, because this is a class variable, a simple solution is to turn your showDialog variable into a getter, this means showDialog will compute again every time you call it, which is not ideal but I don't think it will be terrible for this specific use-case.
ShowDialog get showDialog => ShowDialog(callback: myFunction);
note the get keyword and the => instead of an equal sign
EDIT:
You can also pass the callback as part of the myDialog method, this is probably actually a better idea:
Widget myDialog(VoidCallback callback) {
return showDialog(
builder: (BuildContext context) {
return SimpleDialog(
backgroundColor: Colors.deepPurple[900],
titleTextStyle: const TextStyle(
color: Colors.red, fontSize: 18),
children: [
ElevatedButton(
onPressed: callback,
child: const Text("tab")
),
],
)
},
);
}

I wrote this function that returns a Dialog in a separate file :
Future myDialog({required BuildContext dialogContext, required Function function}){
return showDialog(
context: dialogContext,
builder: (BuildContext context) {
return SimpleDialog(
backgroundColor: Colors.deepPurple[900],
titleTextStyle: const TextStyle(
color: Colors.red, fontSize: 18),
children: [
ElevatedButton(
onPressed: () {
function();
},
child: const Text("tab")
),
],
);
},
);
}
then I made 2 functions, the first one just print (Hello first function) and the second function print (Hello Second function) and set the state and rebuild the widget tree
I made 2 TextButton: the first TextButton call the myDialog function and pass the firstFunction as a parameter, the second TextButton call the myDialog function and pass the secondFunction as a parameter :
the code :
class ExampleState extends State<Example > {
void firstFunction(){
print('Hello first function');
}
void secondFunction(){
setState(() {
print('Hello Second function');
});
}
#override
Widget build(BuildContext context) {
print('build');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
children: [
TextButton(
onPressed: (){
myDialog(dialogContext: context, function: firstFunction );
},
child: Text('First Dialog')
),
TextButton(
onPressed: (){
myDialog(dialogContext: context, function: secondFunction );
},
child: Text('Second Dialog')
)
],
),
)
);
}
}
notice that I added a print('build'); so I can know when it rebuild the widget tree
The result:

Related

How to show next page (Stateless widget) on click only in specific Container in SplitView, not all over the page

I have TestApp, where I have SplitView with 2 horizontal Containers. By clicking button in the first container on the left(blue) I want to show new page (DetailPage widget) but not all over the page, but only in the first Container. Now it shows on the whole screen. What is a best approach to do it?
import 'package:flutter/material.dart';
import 'package:split_view/split_view.dart';
void main() {
runApp(MaterialApp(
title: 'Test',
home: TestApp(),
));
}
class TestApp extends StatelessWidget {
const TestApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SplitView(
children: [
Container(
color: Colors.blue,
child: ElevatedButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => DetailPage()));
},
child: const Text('CLICK')),
),
Container(color: Colors.yellow),
],
viewMode: SplitViewMode.Horizontal,
indicator: SplitIndicator(viewMode: SplitViewMode.Horizontal),
activeIndicator: SplitIndicator(
viewMode: SplitViewMode.Horizontal,
isActive: true,
),
controller: SplitViewController(limits: [null, WeightLimit(max: 1)]),
),
);
}
}
class DetailPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('')), body: Container(color: Colors.red));
}
}
When pushing a new page you will be overriding the old one, meaning the new page will not have a spiltView, the best way to do this is by changing the widget displayed inside of the splitView like this :
import 'package:flutter/material.dart';
import 'package:split_view/split_view.dart';
void main() {
runApp(MaterialApp(
title: 'Test',
home: TestApp(),
));
}
class TestApp extends StatefulWidget { // I have already changed the widgte to stateful here
const TestApp({Key? key}) : super(key: key);
#override
_TestAppState createState() => _TestAppState();
}
class _TestAppState extends State<TestApp> {
#override
Widget build(BuildContext context) {
bool Bool;
return MaterialApp(
home: SplitView(
children: [
if (Bool == false){
Container(
color: Colors.blue,
child: ElevatedButton(
onPressed: () {
setState(() {
Bool = !Bool; // this the method for inverting the boolean, it just gives it the opposite value
});
},
child: const Text('CLICK')),
),
}
else{
DetailPage()
},
Container(color: Colors.yellow),
],
viewMode: SplitViewMode.Horizontal,
indicator: SplitIndicator(viewMode: SplitViewMode.Horizontal),
activeIndicator: SplitIndicator(
viewMode: SplitViewMode.Horizontal,
isActive: true,
),
controller: SplitViewController(limits: [null, WeightLimit(max: 1)]),
),
);
}
}
class DetailPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('')), body: Container(color: Colors.red));
}
}
Above I defined a bool called Bool, when rendering the page it checks if Bool is false, in that case it returns the blue widget, if it is true then it returns the red one, and when you click on the button it inverts the bool and updates the page.
Please note that for updating the page you have to use setState which rebuilds the widget, and to use it you have to use a stateful widget since stateless widget is static and cannot be changed.
Also I haven't tested the code because I don't have split_view package, but you should be able to copy and paste it just fine, if you get any errors please let me know.
When you use Navigator.push your routing to a new page and creating a new state. I think you should use showGeneralDialog instead.
showGeneralDialog(
context: context,
pageBuilder: (BuildContext context,
Animation<double> animation, Animation<double> pagebuilder) {
return Align(
alignment: Alignment.centerLeft,
child: Card(
child: Container(
alignment: Alignment.topLeft,
color: Colors.amber,
//show half the screen width
width: MediaQuery.of(context).size.width / 2,
child: IconButton(
icon: const Icon(Icons.cancel),
onPressed: () {
Navigator.pop(context);
}))),
);
});
try to create new Navigator within Container:
GlobalKey<NavigatorState> _navKey = GlobalKey();
home: SplitView(
children: [
Container(
child: Navigator(
key: _navKey,
onGenerateRoute: (_) => MaterialPageRoute<dynamic>(
builder: (_) {
return Container(
color: Colors.blue,
child: ElevatedButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => DetailPage()));
},
child: const Text('CLICK')),
);
},
),
),),

How to 'setstate' an element from one AlertDialog

I made two forms with pass fields on both, then made all the code to, when the user clicks in the eye Icon, the field show the password, clicking again it hide the password.
But now I put these forms inside an Alert Dialog widget and now it doesn't updating when I click in the icon, only updates if I close the dialog and open again (you open the dialog, click in the icon, it doesn't change. If you close and open again you see the icon changed)
After some search I tried Stateful Builder but it doesn't work too.
Dialog:
Future<void> _myDialog(child){
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return SingleChildScrollView(
child: AlertDialog(
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Padding(
padding: EdgeInsets.all(20),
child: child,
);
},
),
insetPadding: EdgeInsets.only(left: 10, right: 10),
),
);
}
);
}
Toggle method referenced in my textFields:
void _toggle(int index) {
setState(() {
_toggleList[index] = !_toggleList[index];
});
}
How can I toggle it instantly when the user click in the icon as outside the alert?
Edit
Row _showButtons(){
return Row(
children: [
RaisedButton(
child: Text("Change email"),
onPressed: () {_myDialog(_showEmailFields());}
),
RaisedButton(
child: Text("Change pass"),
onPressed: () {_myDialog(_showPassFields());}
),
],
);
}
I have created a structure for you that you should use for achieving what you want here. Make your forms into a separate stateful widgets, so that they have their own State, and you call the right setState function. Right now the setState function you are calling does not belongs to the state of your alert dialog.
class Test extends StatefulWidget {
#override
State<StatefulWidget> createState() => _TestState();
}
class _TestState extends State<Test> {
void showForm() {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Login"),
content: LoginWidget(),
);
},
);
}
#override
Widget build(BuildContext context) {
TextTheme textTheme = Theme.of(context).textTheme;
return Scaffold(
appBar: AppBar(
title: Text("Appbar"),
),
body: Center(
child: RaisedButton(
child: Text("Show Form"),
onPressed: showForm,
),
),
);
}
}
class LoginWidget extends StatefulWidget {
LoginWidget({Key key}) : super(key: key);
#override
LoginWidgetState createState() => LoginWidgetState();
}
class LoginWidgetState extends State<LoginWidget> {
GlobalKey<FormState> _formKey;
bool _passwordVisible;
#override
void initState() {
super.initState();
_formKey = GlobalKey<FormState>();
_passwordVisible = false;
}
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: "Email"),
),
SizedBox(
height: 10,
),
TextFormField(
obscureText: !_passwordVisible,
decoration: InputDecoration(
labelText: "Password",
suffixIcon: IconButton(
onPressed: () {
setState(() {
_passwordVisible = !_passwordVisible;
});
},
icon: Icon(
_passwordVisible ? Icons.visibility : Icons.visibility_off),
),
),
),
],
),
);
}
}

Initstate isn't referenced Flutter Problem

i am facing a new problem with my code, and honestly, I cant figure out where is my mistake , i have made the exact same widget in another file, and runs perfectly.
I'm starting to believe that there is one problem with some widgets maybe.
I paste my code so you can check it out and tell me where is my mistake (very common ) or maybe is some widget/ line of code that is breaking the code.
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(MaterialApp(
home: Scaffold(
body: Products(),
),
));
}
class Products extends StatefulWidget {
Products({Key key}) : super(key: key);
#override
_ProductsState createState() => _ProductsState();
}
Class _ProductsState extends State<Products> {
#override
Widget build(BuildContext context) {
bool valoractual;
#override
void initState() {
super.initState();
valoractual = false;
}
return Scaffold(
appBar: AppBar(
backgroundColor: Color.fromRGBO(239, 180, 185, 1),
actions: <Widget>[
Icon(
Icons.search,
size: 25,
),
Switch(
activeColor: Colors.white,
inactiveThumbColor: Colors.blue[900],
value: valoractual,
onChanged: (bool cambio) {
setState(() {
valoractual = cambio;
});
//cambiovalor();
if (valoractual) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
content: Text(" delete option"),
actions: [
FlatButton(
onPressed: () {
print("****************");
print(valoractual);
Navigator.of(context).pop();
return valoractual;
},
child: Text("Continue"),
)
],
),
);
} else {
showDialog(
context: context,
builder: (context) => AlertDialog(
content:
Text("view option"),
actions: [
FlatButton(
onPreenter code heressed: () {
print("****************");
print(valoractual);
Navigator.of(context).pop();
return valoractual;
},
child: Text("Aceptar"),
)
],
),
);
}
},
),
Icon(Icons.delete, size: 20),
],
),
body: Container(
margin: EdgeInsets.only(top: 10),
child: Text("this is sample text"),
),
);
}
}
used the following code style
Happy Coding :)
try to put your initState function out of the build function
like
Class _ProductsState extends State<Products> {
bool valoractual;
#override
void initState() {
super.initState();
valoractual = false;
}
#override
Widget build(BuildContext context) {
return Scaffold(

Showing a dialog that is in another file in flutter app

I'm trying to show a dialog box that is in another file in a StatefullWidget but when I call its function nothing is happening.
The reason I want to do this is because there is too much nesting of code in my code so I want to keep things simple and clean.
Below is the dialog.dart file.
import 'package:flutter/material.dart';
class PersonDetailsDialog extends StatefulWidget {
PersonDetailsDialog({Key key}) : super(key: key);
#override
_PersonDetailsDialogState createState() {
return _PersonDetailsDialogState();
}
}
class _PersonDetailsDialogState extends State<PersonDetailsDialog> {
#override
Widget build(BuildContext context) {
Future<void> _neverSatisfied() async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text('Rewind and remember'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('You will never be satisfied.'),
Text('You\’re like me. I’m never satisfied.'),
],
),
),
actions: <Widget>[
FlatButton(
child: Text('Regret'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
}
Below is the main.dart file.
mport 'package:flutter/material.dart';
import 'package:practical_0/homepage.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue
),
home: Homepage(),
);
}
}
Below is homepage.dart file where I'm trying to show the dialog when the user clicks RaisedButton but nothing happens.
import 'package:flutter/material.dart';
class Homepage extends StatelessWidget {
final double heightFactor = 600/896;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: RaisedButton(
onPressed: PersonDetailsDialog(), // show dialog
),
),
);
}
}
You have to use ShowDialog Where You want to show dialog.
I hope that following example clear your idea.
class Delete extends StatefulWidget {
#override
_DeleteState createState() => _DeleteState();
}
class _DeleteState extends State<Delete> {
BuildContext parent, child;
#override
Widget build(BuildContext context) => Scaffold(
body: Container(
child: Center(
child: RaisedButton(
onPressed: () {
showDialog(
context: context,
barrierDismissible: false,
child: PersonDetailsDialog());
}, // show dialog
),
),
),
);
}
class PersonDetailsDialog extends StatefulWidget {
PersonDetailsDialog({Key key}) : super(key: key);
#override
_PersonDetailsDialogState createState() {
return _PersonDetailsDialogState();
}
}
class _PersonDetailsDialogState extends State<PersonDetailsDialog> {
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('Rewind and remember'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('You will never be satisfied.'),
Text('You\’re like me. I’m never satisfied.'),
],
),
),
actions: <Widget>[
FlatButton(
child: Text('Regret'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
}
}
Here is an example:
Show dialog is an async function
child: RaisedButton(
onPressed: () async{
final result = await showDialog(
context: context,
builder: (_) => AlertWidget(),
);
return result;
},

flutter: Another exception was thrown: No MaterialLocalizations found

I am trying to show an Alert Dialog on press of a button in Flutter.
Following is my code
main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Different Widgets",
debugShowCheckedModeBanner: false,
home: showAlertDialog()
);
}
void _dialogResult(String value) {
if (value == "YES") {
print("YES");
} else {
print("NO");
}
Navigator.pop(context);
}
Widget showAlertDialog() {
TextEditingController textEditingController = TextEditingController();
return Scaffold(
appBar: AppBar(
title: Text("Different Widgets"),
),
body: Container(
child: Center(
child: Column(
children: <Widget>[
TextField(
controller: textEditingController,
),
RaisedButton(
onPressed: () {
print("Hi");
AlertDialog dialog = AlertDialog(
title: Text("Hi"),
content: Text(
textEditingController.text,
style: TextStyle(fontSize: 30.0),
),
actions: <Widget>[
FlatButton(
onPressed: () {
_dialogResult("YES");
},
child: Text("YES")),
FlatButton(
onPressed: () {
_dialogResult("NO");
},
child: Text("NO")),
],
);
showDialog(context: context, builder: (BuildContext context) => dialog);
},
child: Text("Click Me"),
)
],
),
),
),
);
}
What does this has to do with Localisation, I cannot follow. I did the same steps as per the docs. I am able to see the button but on click of that button I keep getting error. I tried writing print statement inside of button click and the print statement appears in the log, definitely something wrong with AlertDialog.
You may get No MaterialLocalizations found error while showing dialog using showDialog() class in Flutter. The issue is putting child widget on home property of MaterialApp() widget without creating new widget class.
One way to solve is putting MaterialApp() inside runApp() and create new class for home property.
import 'package:flutter/material.dart';
main() {
runApp(
MaterialApp(
home: MyApp(),
title: "Different Widgets",
debugShowCheckedModeBanner: false,
),
);
}
/*
place MaterialApp() widget on runApp() and create
new class for its 'home' property
to escape 'No MaterialLocalizations found' error
*/
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return showAlertDialog();
}
void _dialogResult(String value) {
if (value == "YES") {
print("YES");
} else {
print("NO");
}
Navigator.pop(context);
}
Widget showAlertDialog() {
TextEditingController textEditingController = TextEditingController();
return Scaffold(
appBar: AppBar(
title: Text("Different Widgets"),
),
body: Container(
child: Center(
child: Column(
children: <Widget>[
TextField(
controller: textEditingController,
),
RaisedButton(
onPressed: () {
print("Hi");
AlertDialog dialog = AlertDialog(
title: Text("Hi"),
content: Text(
textEditingController.text,
style: TextStyle(fontSize: 30.0),
),
actions: <Widget>[
FlatButton(
onPressed: () {
_dialogResult("YES");
},
child: Text("YES")),
FlatButton(
onPressed: () {
_dialogResult("NO");
},
child: Text("NO")),
],
);
showDialog(
context: context,
builder: (BuildContext context) => dialog);
},
child: Text("Click Me"),
)
],
),
),
),
);
}
}