Related
I use sqflite for flutter database management. In particular, I would like the user to enter the data only once and therefore I would need to hide and disable the button only once the data has been entered. How can I do?
Home Page, where is the button
class Casa extends StatefulWidget {
static const routeName = '/';
#override
_CasaState createState() => _CasaState();
}
class _CasaState extends State<Casa> {
DataRepository _repository;
#override
void initState() {
super.initState();
_repository = SqlRepository();
}
#override
void dispose() async {
await _repository.closeDB();
super.dispose();
}
void getNewItem(BuildContext context) async {
Attivita newItem =
await Navigator.pushNamed<Attivita>(context, AddItemScreen.routeName);
if (newItem != null) {
await _repository.add(newItem);
setState(() {});
}
}
void switchAndUpdate(Attivita item) async {
await _repository.put(item.id, item);
setState(() {});
}
void delete(Attivita item) async {
await _repository.delete(item.id);
setState(() {});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: Colors.lightBlue[900],
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add, color: Colors.lightBlue[900],),
backgroundColor: Colors.white,
onPressed: () {
getNewItem(context);
},
),
body:
FutureBuilder<List<Attivita>>(
future: _repository.getAll(),
builder:
(BuildContext context, AsyncSnapshot<List<Attivita>> snapshot) {
return ListView.builder(
padding: EdgeInsets.all(8),
itemCount: snapshot.data == null ? 0 : snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Dismissible(
key: UniqueKey(),
background: DecoratedBox(
decoration: BoxDecoration(color: Colors.red),
child: Align(
alignment: Alignment(-0.9, 00),
child: Icon(
Icons.delete,
color: Colors.white,
),
),
),
direction: DismissDirection.startToEnd,
onDismissed: (direction) {
Attivita item = snapshot.data[index];
snapshot.data.removeAt(index);
delete(item);
},
child: Card(
child: ListTile(
title: Text(snapshot.data[index].nome, style: TextStyle(color: Colors.lightBlue[900]),),
onTap: () {
Navigator.pushNamed(context, DettaglioScreen.routeName, arguments: snapshot.data[index]);
},
onLongPress: () {
switchAndUpdate(snapshot.data[index]);
},
),
),
);
},
);
},
)
),
);
}
}
so i have to add some details, because it is written that "it looks like your post is mostly code; please add some more details"
To check if user put so data you can use:
Use sharedPreferences and store bool value if user entered data.
On initState check if database contains data if yes it means user put some data and you can hide button.
I don't use Sqlite in flutter but I think you can use
List<Map<String, dynamic>> result;
result = await db.query(tableName);
isNotData = result.isEmpty;
Or something similar :) You can do it
Choose one way and store bool value eg. in bool isNotData
When you will have bool value about data you can write:
In _CasaState above initState: bool isNotData;
and in Scaffold in floatingActionButton property:
Scaffold(
appBar: AppBar(
title: Text('Material App Bar'),
),
floatingActionButton: isNotData
? FloatingActionButton(
onPressed: () {},
)
: null,
body: Center(
),
),
),
I am a newbie in using flutter. I am try to create some sample application in flutter for create own application. I found a nice application in flutter with bloc pattern. please see the following link.
it's just a login page. there is a email and password textbox in the form with validation. One validator for email and another validator for the password length. There is a submit button in the form, initially it's disabled, if the email and password successfully validated then the submit button enabled. it uses the bloc pattern architecture with rxdart package.
I have a issue in submit button validation, after enter the email and password field it's not enabled the button field.
button validation code
Stream<bool> get submitCheck =>
Rx.combineLatest2(email, password, (e, p) => true);
Please see the below code.
main.dart
import 'package:bloc_login/pagetwo.dart';
import 'package:flutter/material.dart';
import 'package:bloc_login/bloc.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(title: 'Flutter Demo Home Page'),
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.teal,
),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
changethePage(BuildContext context) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => PageTwo()));
}
#override
Widget build(BuildContext context) {
final bloc = Bloc();
return Scaffold(
appBar: AppBar(
title: Text("Bloc pattern"),
),
body: SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height,
padding: EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
StreamBuilder<String>(
stream: bloc.email,
builder: (context, snapshot) => TextField(
onChanged: bloc.emailChanged,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter Email",
labelText: "Email",
errorText: snapshot.error),
),
),
SizedBox(
height: 20,
),
StreamBuilder<String>(
stream: bloc.password,
builder: (context, snapshot) => TextField(
onChanged: bloc.passwordChanged,
keyboardType: TextInputType.text,
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter password",
labelText: "Password",
errorText: snapshot.error),
),
),
SizedBox(
height: 20,
),
StreamBuilder<bool>(
stream: bloc.submitCheck,
builder: (context, snapshot) => RaisedButton(
color: Colors.tealAccent,
onPressed: (snapshot.data != null)
? () => changethePage(context)
: null,
child: Text("Submit"),
))
],
),
),
),
);
}
}
bloc.dart
import 'dart:async';
import 'package:bloc_login/validator.dart';
import 'package:rxdart/rxdart.dart';
class Bloc extends Object with Validators implements BaseBloc {
final _emailController = StreamController<String>();
final _passwordController = StreamController<String>();
Function(String) get emailChanged => _emailController.sink.add;
Function(String) get passwordChanged => _passwordController.sink.add;
Stream<String> get email => _emailController.stream.transform(emailValidator);
Stream<String> get password =>
_passwordController.stream.transform(passwordValidator);
Stream<bool> get submitCheck =>
Rx.combineLatest2(email, password, (e, p) => true);
#override
void dispose() {
_emailController.close();
_passwordController.close();
}
}
abstract class BaseBloc {
void dispose();
}
validator.dart
import 'dart:async';
mixin Validators {
var emailValidator =
StreamTransformer<String, String>.fromHandlers(handleData: (email, sink) {
if (email.contains("#")) {
sink.add(email);
} else {
sink.addError("Email is not valid.");
}
});
var passwordValidator = StreamTransformer<String, String>.fromHandlers(
handleData: (password, sink) {
if (password.length > 4) {
sink.add(password);
} else {
sink.addError("Password length should be greater than 4.");
}
});
}
Please advice.
Combinelatest2 are fine, the problem is that you are creating a new bloc in each rebuild.
So create the bloc in the initState method and dispose the bloc in the dispose method of the StatefulWidget.
But also your StreamControllers have a Stream that supports only one single subscriber, so if you want that theStream of the StreamControllercan be listened to more than once, this need to be a broadcast stream, one of the ways to do it is using the StreamController .broadcast () constructor.
P.D. If you are creating forms with the bloc pattern you can check flutter_form_bloc, it saves you a lot of code.
Since dart 2.1 you don't need to extend an object to use a mixin
class Bloc with Validators implements BaseBloc {
final _emailController = StreamController<String>.broadcast();
final _passwordController = StreamController<String>.broadcast();
//...
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Bloc _bloc;
#override
void initState() {
super.initState();
_bloc = Bloc();
}
#override
void dispose() {
_bloc.dispose();
super.dispose();
}
changethePage(BuildContext context) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => PageTwo()));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Bloc pattern"),
),
body: SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height,
padding: EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
StreamBuilder<String>(
stream: _bloc.email,
builder: (context, snapshot) => TextField(
onChanged: _bloc.emailChanged,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter Email",
labelText: "Email",
errorText: snapshot.error),
),
),
SizedBox(
height: 20,
),
StreamBuilder<String>(
stream: _bloc.password,
builder: (context, snapshot) => TextField(
onChanged: _bloc.passwordChanged,
keyboardType: TextInputType.text,
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter password",
labelText: "Password",
errorText: snapshot.error),
),
),
SizedBox(
height: 20,
),
StreamBuilder<bool>(
stream: _bloc.submitCheck,
builder: (context, snapshot) => RaisedButton(
color: Colors.tealAccent,
onPressed: (snapshot.data != null)
? () => changethePage(context)
: null,
child: Text("Submit"),
))
],
),
),
),
);
}
}
This is the function I'm using to get a form displayed on my screen.
Form getNewLoanForm() {
return new Form(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new TextFormField(
decoration: new InputDecoration(labelText: 'Loan name'),
validator: (value) {
if (value.isEmpty) {
return 'Enter name';
} else {
return null;
}
},
onSaved: (value) => _loanName = value,
),
new Container(height: 16.0),
new RaisedButton(
child: new Text(
'ADD',
style: new TextStyle(color: Colors.white, fontSize: 17.0),
),
onPressed: addNewLoan,
color: Colors.blue,
)
],
),
);
}
Everything works fine up unit now. The problem starts when I add
final formKey = new GlobalKey<FormState>();
I declare this varial in class level. and use it in form.
return new Form(
key: formKey,
Like this and now the TextFormField starts to behave weirdly. Now if I select the text field it deselects itself.
Now keep in mind that this doesn't happen if I make this page my root page. But I'm pushing this page from my root page. If I make this page my root page the problem doesn't occur there.
Any idea what's wrong here?
Okay I'm putting my whole class
import 'package:flutter/material.dart';
import 'package:datetime_picker_formfield/datetime_picker_formfield.dart';
import 'package:intl/intl.dart';
class NewLoan extends StatelessWidget {
String _loanName;
String _dateCreated;
final formKey = new GlobalKey<FormState>();
void addNewLoan() {}
Form getNewLoanForm(BuildContext context) {
final dateFormat = DateFormat("dd/MM/yyyy");
return new Form(
// key: formKey,
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new TextFormField(
decoration: new InputDecoration(labelText: 'Loan name'),
validator: (value) {
if (value.isEmpty) {
return 'Enter name';
} else {
_loanName = value;
return null;
}
},
),
new Container(height: 16.0),
DateTimePickerFormField(
decoration: new InputDecoration(labelText: 'Date'),
format: dateFormat,
dateOnly: true,
onChanged: (date) {
print('Selected date ${date.toString()}');
Scaffold
.of(context)
.showSnackBar(SnackBar(content: Text('$date')));
},
),
new Container(height: 16.0),
new RaisedButton(
child: new Text(
'ADD',
style: new TextStyle(color: Colors.white, fontSize: 17.0),
),
onPressed: addNewLoan,
color: Colors.blue,
)
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: new Text('New Loan'),
),
body: new Container(
padding: new EdgeInsets.all(20.0),
child: getNewLoanForm(context),
),
);
}
}
My Root page
import 'dart:async';
import 'package:firebase/new_loan.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'auth.dart';
import 'current_balance_page.dart';
import 'dart:io' show Platform;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_database/ui/firebase_animated_list.dart';
class HomePage extends StatefulWidget {
HomePage({this.auth, this.onSignOut});
final BaseAuth auth;
final VoidCallback onSignOut;
#override
_HomePageState createState() => _HomePageState();
}
FirebaseApp app;
Future<FirebaseApp> firebaseApp() async {
final FirebaseApp app = await FirebaseApp.configure(
name: 'hisab-442a2',
options: Platform.isIOS
? const FirebaseOptions(
googleAppID: '1:18726312:ios:1b7829f2ac180d28',
gcmSenderID: '75461431231231291692',
databaseURL: 'https://hisab-44s2a2.firebaseio.com',
)
: const FirebaseOptions(
googleAppID: '1:297855924061:android:669871c998cc21bd',
apiKey: 'AIzaSyD_shO5mfO9lhy2TVWhfo1VUmARKlG4suk',
databaseURL: 'https://hisab-442a2.firebaseio.com',
),
);
return app;
}
enum Balancestatus { checking, found, noBalance }
class _HomePageState extends State<HomePage> {
Balancestatus status = Balancestatus.checking;
DatabaseReference _databaseRef;
#override
void initState() {
super.initState();
getDatabaseRef();
}
void getDatabaseRef() async {
app = await firebaseApp();
final FirebaseDatabase database = new FirebaseDatabase(app: app);
_databaseRef = database.reference();
_databaseRef.child('ccurrent_balance').once().then((DataSnapshot snapshot) {
print('Current balance ${snapshot.value}');
int balance = snapshot.value;
setState(() {
if (balance == null || balance == 0) {
status = Balancestatus.noBalance;
} else {
status = Balancestatus.found;
}
});
});
}
Future _signOut() async {
try {
await widget.auth.singOut();
widget.onSignOut();
} catch (e) {
print('Error signing out: $e');
}
}
void balanceSet() {
setState(() {
status = Balancestatus.checking;
});
}
#override
Widget build(BuildContext context) {
switch (status) {
case Balancestatus.checking:
return new WelcomePage(onSignOut: _signOut);
case Balancestatus.found:
// return new NewLoan();
return new LoanList(onSignOut: _signOut);
case Balancestatus.noBalance:
return CurrentBalancePage(
databaseRef: _databaseRef,
balanceSet: balanceSet,
);
default:
return new WelcomePage(onSignOut: _signOut);
}
}
}
class LoanList extends StatelessWidget {
final VoidCallback onSignOut;
LoanList({this.onSignOut});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: Text('Loans'),
actions: <Widget>[
new FlatButton(
child: new Text(
'Logout',
style: new TextStyle(fontSize: 17.0, color: Colors.white),
),
onPressed: onSignOut,
)
],
),
body: new Container(
padding: EdgeInsets.all(20.0),
alignment: Alignment.bottomCenter,
child: new RaisedButton(
child: new Text(
'New Loan',
style: new TextStyle(color: Colors.white, fontSize: 17.0),
),
onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => NewLoan()));
},
color: Colors.blue,
),
),
);
}
void addNewLoan() {
print('Add new Loan');
}
}
class WelcomePage extends StatelessWidget {
final VoidCallback onSignOut;
WelcomePage({this.onSignOut});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: new Text('Welcome'),
actions: <Widget>[
new FlatButton(
child: new Text(
'Logout',
style: new TextStyle(fontSize: 17.0, color: Colors.white),
),
onPressed: onSignOut,
)
],
),
body: new Container(
child: new Center(
child: new Text(
'Checking',
style: new TextStyle(fontSize: 32.0),
),
),
),
);
}
}
I need a call the user attention to a button. The first idea that came to mind is to add a blink animation. I really don't know how to do that, but I tried to make it work with the following code:
Timer timer = new Timer(new Duration(seconds: 1), () {
//basic logic to change the color variable here
setState(() {});
});
It is straightforward, every second setState is called and the widget is created again.
But it doesn't work, the timer is called only once. And, besides that, calling setState within a Timer seems wrong to me.
There is a better approach to this?
You can achieve this in an easy way using AnimationController and FadeTransition widget, here you have the code:
class MyBlinkingButton extends StatefulWidget {
#override
_MyBlinkingButtonState createState() => _MyBlinkingButtonState();
}
class _MyBlinkingButtonState extends State<MyBlinkingButton>
with SingleTickerProviderStateMixin {
AnimationController _animationController;
#override
void initState() {
_animationController =
new AnimationController(vsync: this, duration: Duration(seconds: 1));
_animationController.repeat(reverse: true);
super.initState();
}
#override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _animationController,
child: MaterialButton(
onPressed: () => null,
child: Text("Text button"),
color: Colors.green,
),
);
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
}
Usage:
main() {
runApp(
MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Material(
child: Center(
child: MyBlinkingButton(),
),
),
),
);
}
DartPad example
Result:
You can do that with this approach also.
My logic is a little different I am using alternate for animation. Once animation completed in forward I'm coming backward.
Which is good for eyesight
ie:
forward -> backward
backward -> forward
and so on
import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Wordpress App',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new BlinkAnimation(),
);
}
}
class BlinkAnimation extends StatefulWidget {
#override
_BlinkAnimationState createState() => _BlinkAnimationState();
}
class _BlinkAnimationState extends State<BlinkAnimation>
with SingleTickerProviderStateMixin {
Animation<Color> animation;
AnimationController controller;
initState() {
super.initState();
controller = AnimationController(
duration: const Duration(milliseconds: 500), vsync: this);
final CurvedAnimation curve =
CurvedAnimation(parent: controller, curve: Curves.linear);
animation =
ColorTween(begin: Colors.white, end: Colors.blue).animate(curve);
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
setState(() {});
});
controller.forward();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: new Text('Blink Animation'),
),
body: new Center(
child: AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
return new Container(
child: new RaisedButton(
color: animation.value,
onPressed: () {
controller.forward();
},
child: Text('Blink Animation'),
),
);
},
),
),
);
}
dispose() {
controller.dispose();
super.dispose();
}
}
Here is the example from the answer by #nitishk72 but with code updated to null safety and more recent Flutter version.
Result:
Code:
You can just copy this to DartPad.dev and it will work :)
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Blink animation demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: BlinkAnimation(),
);
}
}
class BlinkAnimation extends StatefulWidget {
#override
_BlinkAnimationState createState() => _BlinkAnimationState();
}
class _BlinkAnimationState extends State<BlinkAnimation>
with SingleTickerProviderStateMixin {
late Animation<Color?> animation;
late AnimationController controller;
#override
initState() {
super.initState();
controller = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
final CurvedAnimation curve =
CurvedAnimation(parent: controller, curve: Curves.linear);
animation =
ColorTween(begin: Colors.white, end: Colors.blue).animate(curve);
// Keep the animation going forever once it is started
animation.addStatusListener((status) {
// Reverse the animation after it has been completed
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
setState(() {});
});
// Remove this line if you want to start the animation later
controller.forward();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Blink Animation'),
),
body: Center(
child: AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget? child) {
return Container(
color: animation.value,
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
// Start the animation or do something else on click
// controller.forward();
print('button does something!');
},
child: const Text('Blink Animation'),
),
);
},
),
),
);
}
#override
dispose() {
controller.dispose();
super.dispose();
}
}
Current answers are great but do not cover opacity blink (repeated fade in, fade out), you may use the following widget if that's your goal:
class BlinkAnimation extends StatefulWidget {
final Widget child;
const BlinkAnimation({
Key? key,
required this.child,
}) : super(key: key);
#override
State<BlinkAnimation> createState() => _BlinkAnimationState();
}
class _BlinkAnimationState extends State<BlinkAnimation>
with SingleTickerProviderStateMixin {
late final AnimationController controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..repeat(reverse: true);
late final Animation<double> animation = CurvedAnimation(
parent: controller,
curve: Curves.easeIn,
);
#override
Widget build(BuildContext context) {
return FadeTransition(opacity: animation, child: widget.child);
}
#override
dispose() {
controller.dispose();
super.dispose();
}
}
based on FadeTransition
I'm trying to create a Radio in a showDialog, however the animation that occurs on Radio does not appear in showDialog.
For example: when tapped in foo2 nothing happens, and when you exit in showDialog and go back to it, foo2 is selected.
Below is the code and a gif showing what is happening:
import "package:flutter/material.dart";
void main() {
runApp(new ControlleApp());
}
class ControlleApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: "My App",
home: new HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
HomePageState createState() => new HomePageState();
}
enum _RadioGroup {
foo1,
foo2
}
class HomePageState extends State<HomePage> {
_RadioGroup _itemType = _RadioGroup.foo1;
void changeItemType(_RadioGroup type) {
setState(() {
_itemType = type;
});
}
void showDemoDialog<T>({ BuildContext context, Widget child }) {
showDialog<T>(
context: context,
child: child,
);
}
#override
Widget build(BuildContext context){
return new Scaffold(
appBar: new AppBar(backgroundColor: new Color(0xFF26C6DA)),
body: new Container(
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new InkWell(
onTap: (){
showDemoDialog<String>(
context: context,
child: new SimpleDialog(
title: const Text("show"),
children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Radio<_RadioGroup>(
groupValue: _itemType,
value: _RadioGroup.foo1,
onChanged: changeItemType
),
const Text("foo1"),
new Radio<_RadioGroup>(
groupValue: _itemType,
value: _RadioGroup.foo2,
onChanged: changeItemType
),
const Text("foo2"),
],
)
],
)
);
},
child: new Container(
margin: new EdgeInsets.only(top: 16.0, bottom: 8.0),
child: new Text("Show"),
),
)
],
),
)
);
}
}
Remember that components are immutable.
When you call showDialog, the content of that dialog won't change even if HomePage does.
The solution is easy. You need to refactor a bit your code to something like :
showDialog(
context: context,
builder: (context) => MyForm()
)
and instead of changing the state of HomePage, you instead change the state of MyForm.
example :
class Test extends StatelessWidget {
void onSubmit(String result) {
print(result);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
onPressed: () => showDialog(context: context, builder: (context) => MyForm(onSubmit: onSubmit)),
child: Text("dialog"),
),
),
);
}
}
typedef void MyFormCallback(String result);
class MyForm extends StatefulWidget {
final MyFormCallback onSubmit;
MyForm({this.onSubmit});
#override
_MyFormState createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
String value = "foo";
#override
Widget build(BuildContext context) {
return SimpleDialog(
title: Text("My form"),
children: <Widget>[
Radio(
groupValue: value,
onChanged: (value) => setState(() => this.value = value),
value: "foo",
),
Radio(
groupValue: value,
onChanged: (value) => setState(() => this.value = value),
value: "bar",
),
FlatButton(
onPressed: () {
Navigator.pop(context);
widget.onSubmit(value);
},
child: new Text("submit"),
)
],
);
}
}