I have a very simply scenario. A button where when pushed would transition to the next screen. In Navigator 1.0, it is very simple to do by:
ElevatedButton(
onPressed: () {
// Navigate to next screen.
Navigator.push(
context,
MaterialPageRoute(builder: (context) => NextScreen()),
);
},
child: Text('Next Screen!'),
)
It seems that with Navigator 2.0 I would need to have a state to keep track of the current screen.
...
Navigator(
pages: [
MainScreenPageRoute()
if (state.isNextScreen) {
NextScreenPageRoute()
}
],
onPopPage: (route, result) {
// would have to keep track of this value
state.isNextScreen = false;
return route.didPop(result);
},
)
...
As for before I don't have to keep track of a state just to navigate, In Navigator 2.0 it seems that it is required. Is it really the case? If so do you guys have any suggestion of how to handle this properly?
P.S.
It also feels like now I have to keep track of the state which adds to more work compared to before.
yes in navigator 2 you change the page when you change the state.
There are some packages that help you to avoid this like qlevar_router
see this example
import 'package:flutter/material.dart';
import 'package:qlevar_router/qlevar_router.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final books = [
Book('Stranger in a Strange Land', 'Robert A. Heinlein'),
Book('Foundation', 'Isaac Asimov'),
Book('Fahrenheit 451', 'Ray Bradbury'),
];
#override
Widget build(BuildContext context) => MaterialApp.router(
routeInformationParser: QRouteInformationParser(),
routerDelegate: QRouterDelegate([
QRoute(path: '/', builder: () => BooksListScreen(books)),
QRoute(
path:
'/books/:id([0-${books.length - 1}])', // The only available pages are the pages in the list
builder: () => BookDetailsScreen(books[QR.params['id']!.asInt!])),
]));
}
class Book {
final String title;
final String author;
Book(this.title, this.author);
}
class BooksListScreen extends StatelessWidget {
final List<Book> books;
BooksListScreen(this.books);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ListView(
children: [
for (var book in books)
ListTile(
title: Text(book.title),
subtitle: Text(book.author),
onTap: () => QR.to('/books/${books.indexOf(book)}'))
],
),
);
}
}
class BookDetailsScreen extends StatelessWidget {
final Book book;
BookDetailsScreen(this.book);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(book.title, style: Theme.of(context).textTheme.headline6),
Text(book.author, style: Theme.of(context).textTheme.subtitle1),
],
),
),
);
}
}
don't forget to add qlevar_router: 1.4.0 in pubspec.yaml
Related
Flutter Web(Navigator 2.0/Router API):
How to handle authenticated routes and their redirects after successful auth?
e.g.
I have these kind of routes on my system
/book/xyz (authenticated user)
/author/abc/book/xyz (authenticated user)
/authentication (non-authenticated user)
/info (non-authenticated user)
If user opens this URL directly, I wanted to ask user to login first, at that time route will be redirected to..
/authentication
Once logged in, I would like to user to navigate previously opened URL if any else home..
Seems like this kind of things may help, any thoughts - how we can achieve similar things?
https://stackoverflow.com/a/43171515/2145844
I have tried few samples for Navigation 2.0 / Router API, yes I can understand the concepts a bit..
Some of the references, I have looked at..
https://medium.com/flutter/learning-flutters-new-navigation-and-routing-system-7c9068155ade
https://github.com/orestesgaolin/navigator_20_example
https://github.com/flutter/flutter/tree/master/dev/benchmarks/test_apps/stocks
Here is how to do it using VRouter >=1.2
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:vrouter/vrouter.dart';
void main() {
runApp(BooksApp());
}
class Book {
final String title;
final Author author;
Book(this.title, this.author);
}
class Author {
String name;
Author(this.name);
}
class AppState extends ChangeNotifier {
bool _isAuthenticated = false;
bool get isAuthenticated => _isAuthenticated;
void authenticate() {
if (isAuthenticated) return;
_isAuthenticated = true;
notifyListeners();
}
}
class BooksApp extends StatelessWidget {
final List<Book> books = [
Book('Stranger in a Strange Land', Author('Robert A. Heinlein')),
Book('Foundation', Author('Isaac Asimov')),
Book('Fahrenheit 451', Author('Ray Bradbury')),
];
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => AppState(),
child: Builder(
builder: (BuildContext context) {
return VRouter(
routes: [
VWidget(path: '/login', widget: AuthenticationScreen()),
VWidget(path: '/info', widget: InfoWidget()),
VGuard(
beforeEnter: (vRedirector) =>
authenticationCheck(context, vRedirector: vRedirector),
stackedRoutes: [
VWidget(
path: '/',
widget: BooksListScreen(books: books),
stackedRoutes: [
VWidget(
path: r'book/:bookId(\d+)',
widget: Builder(builder: (BuildContext context) {
return BookDetailsScreen(
book: books[int.parse(context.vRouter.pathParameters['bookId']!)],
);
}),
),
],
),
VWidget(
path: '/authors',
widget: AuthorsListScreen(authors: books.map((e) => e.author).toList()),
stackedRoutes: [
VWidget(
path: r'/author/:authorId(\d+)',
widget: Builder(builder: (BuildContext context) {
return AuthorDetailsScreen(
author: books[int.parse(context.vRouter.pathParameters['authorId']!)]
.author,
);
}),
),
],
),
],
),
],
);
},
),
);
}
Future<void> authenticationCheck(BuildContext context, {required VRedirector vRedirector}) async {
if (!Provider.of<AppState>(context, listen: false).isAuthenticated) {
vRedirector.to('/login', queryParameters: {'redirectedFrom': '${vRedirector.toUrl}'});
}
}
}
class AuthenticationScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
onPressed: () {
Provider.of<AppState>(context, listen: false).authenticate();
context.vRouter.to(context.vRouter.queryParameters['redirectedFrom'] == null
? '/'
: context.vRouter.queryParameters['redirectedFrom']!);
},
child: Text('Click to login'),
),
);
}
}
class InfoWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Text('Some info but actually there is nothing'),
);
}
}
class BooksListScreen extends StatelessWidget {
final List<Book> books;
BooksListScreen({required this.books});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ListView(
children: [
for (var book in books)
ListTile(
title: Text(book.title),
subtitle: Text(book.author.name),
onTap: () => context.vRouter.to('/book/${books.indexOf(book)}'),
)
],
),
);
}
}
class AuthorsListScreen extends StatelessWidget {
final List<Author> authors;
AuthorsListScreen({required this.authors});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ListView(
children: [
ElevatedButton(
onPressed: () => context.vRouter.to('/'),
child: Text('Go to Books Screen'),
),
for (var author in authors)
ListTile(
title: Text(author.name),
onTap: () => context.vRouter.to('/author/${authors.indexOf(author)}'),
)
],
),
);
}
}
class BookDetailsScreen extends StatelessWidget {
final Book book;
BookDetailsScreen({required this.book});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(book.title, style: Theme.of(context).textTheme.headline6),
ElevatedButton(
onPressed: () {
context.vRouter.to('/author/${context.vRouter.pathParameters['bookId']}');
},
child: Text(book.author.name),
),
],
),
),
);
}
}
class AuthorDetailsScreen extends StatelessWidget {
final Author author;
AuthorDetailsScreen({required this.author});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(author.name, style: Theme.of(context).textTheme.headline6),
],
),
),
);
}
}
The trick is to use a VGuard which, before entering the stackedRoutes, checks whether or not the user is authenticated.
I used queryParameters to store where the user it redirected from, however you could use historyState if you did not want where the user is redirected from appear in the url. That being said, I still prefer queryParameters in that case since is allows link sharing.
You can use qlevar_router to do this.
from the example, in the link, you can define a Middleware with redirectGuard to check if the user can access this page or its children, otherwise just give the route to redirect to.
you can see this in the example project too.
if you give the right to access to Child 4 it will go to Child 4 otherwise you will be navigated to Child 2.
I'm trying to restore my listview widget after android kills my app from memory by using RootRestorationScope:
runApp(RootRestorationScope(
child: MaterialApp(home: MyApp()),
restorationId: "root",
));
and making my own restorable widget using extends RestorableValue:
class RestorableListBloc extends RestorableValue<ListBloc> {
#override
ListBloc createDefaultValue() {
return ListBloc(
repository: Repository(),
)..add(
Fetch(),
);
}
#override
void didUpdateValue(ListBloc oldValue) {
if (oldValue.state != value.state) {
notifyListeners();
}
}
#override
ListBloc fromPrimitives(Object data) {
print('data: ' + data);
return ListBloc(repository: data);
}
#override
Object toPrimitives() {
return value.repository;
}
}
Where ListBloc is a Bloc that controls what's in my listview widget (collection of timers right now in case anyone's curious).
I extend the class with the restoration mixing and call the bloc inside of a multibloc providers widget as follows:
class _MyAppState extends State<MyApp> with RestorationMixin {
SpeedDialGenerator speedDial = SpeedDialGenerator();
RestorableListBloc _test = RestorableListBloc();
final assetsAudioPlayer = AssetsAudioPlayer();
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) => DrawerCubit(value: false),
child: Scaffold(
backgroundColor: Colors.blue[50],
drawer: SafeArea(
child: AppDrawer(),
),
appBar: AppBar(
backgroundColor: Colors.blue[900],
title: Text('Myapp'),
centerTitle: true,
actions: [
IconButton(
icon: Image.asset('assets/images/myapp_white.png'),
onPressed: () {},
),
],
),
body: Stack(children: [
HourglassBackground(),
MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => _test.value,
),
BlocProvider(
create: (context) => AppStateCubit(),
)
],
child: BlocBuilder<ListBloc, ListState>(
builder: (context, state) {
if (state is Failure) {
return Center(
child: Text('Oops something went wrong!'),
);
}
if (state is Loaded) {
return Stack(children: [
HomePage(state: state, displayNotification: () => {}),
Padding(
padding: EdgeInsets.fromLTRB(0, 0, 10, 10),
child: BlocProvider(
create: (context) => TimerpopupCubit(),
child: speedDial.buildSpeedDial(context)),
),
]);
}
return Center(
child: CircularProgressIndicator(),
);
},
)),
]),
),
);
}
#override
String get restorationId => 'root';
#override
void restoreState(RestorationBucket oldBucket, bool initialRestore) {
registerForRestoration(_test, restorationId);
}
Unfortunately, everytime I run the app I get:
The following assertion was thrown building Builder:
'package:flutter/src/services/restoration.dart': Failed assertion: line 592 pos 12: 'debugIsSerializableForRestoration(value)': is not true.
There's not much documentation and I know this is a relatively new feature of flutter, but from what I gather this means that you can't currently restore a BLoC?
Is there a way around this or some other approach I should look at?
Thanks
There are some approaches such as using HydratedBloc library that has built in feature to save its state.
Or you can implement your own local database and when user re run app, the repository returns first local data and returns remote one.
I'm trying to implement Provider state management on counter application to understand Provider's functionality better. I have added two buttons with respect to two different text widget. So, now whenever I click any of the two widget both the Text widgets get update and give same value. I want both the widgets independent to each other.
I have used ScopedModel already and got the desire result but now I want to try with provider.
Image Link : https://i.stack.imgur.com/ma3tR.png
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("====Home Page Rebuilt====");
return Scaffold(
appBar: AppBar(
title: Text("HomePage"),
),
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
//crossAxisAlignment:CrossAxisAlignment.center,
children: [
Consumer<CounterModel>(
builder: (context, value, child) {
return CustomWidget(
number: value.count.toString(),
);
},
),
Consumer<CounterModel>(
builder: (context, value, child) {
return CustomWidget(
number: value.count.toString(),
);
},
),
],
)),
);
}
}
class CustomWidget extends StatelessWidget {
final String number;
const CustomWidget({Key key, this.number}) : super(key: key);
#override
Widget build(BuildContext context) {
print("====Number Page Rebuilt====");
return ButtonBar(
alignment: MainAxisAlignment.center,
children: [
Consumer<CounterModel>(
builder: (context, value, child) {
return Text(
value.count.toString(),
style: Theme.of(context).textTheme.headline3,
);
},
),
FlatButton(
color: Colors.blue,
onPressed: () =>
Provider.of<CounterModel>(context, listen: false).increment(),
child: Text("Click"),
),
],
);
}
}
If you want them independent from each other, then you need to differentiate them somehow. I have a bit of a different style to implement the Provider and it hasn't failed me yet. Here is a complete example.
You should adapt your implementation to something like this:
Define your provider class that extends ChangeNotifier in a CounterProvider.dart file
import 'package:flutter/material.dart';
class CounterProvider extends ChangeNotifier {
/// You can either set an initial value here or use a UserProvider object
/// and call the setter to give it an initial value somewhere in your app, like in main.dart
int _counter = 0; // This will set the initial value of the counter to 0
int get counter => _counter;
set counter(int newValue) {
_counter = newValue;
/// MAKE SURE YOU NOTIFY LISTENERS IN YOUR SETTER
notifyListeners();
}
}
Wrap your app with a Provider Widget like so
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
/// don't forget to import it here too
import 'package:app/CounterProvider.dart';
void main() {
runApp(
MaterialApp(
initialRoute: '/root',
routes: {
'/root': (context) => MyApp(),
},
title: "Your App Title",
),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
/// Makes data available to everything below it in the Widget tree
/// Basically the entire app.
ChangeNotifierProvider<CounterProvider>.value(value: CounterProvider()),
],
child: MaterialApp(
home: HomeScreen(),
),
);
}
}
Access and update data anywhere in the app
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
/// MAKE SURE TO IMPORT THE CounterProvider.dart file
import 'package:app/CounterProvider.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
CounterProvider counterProvider;
#override
Widget build(BuildContext context) {
/// LISTEN TO THE CHANGES / UPDATES IN THE PROVIDER
counterProvider = Provider.of<CounterProvider>(context);
return Scaffold(
appBar: AppBar(
title: Text("HomePage"),
),
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
//crossAxisAlignment:CrossAxisAlignment.center,
children: [
_showCounterButton(1),
_showCounterButton(2),
],
),
),
);
}
Widget _showCounterButton(int i) {
return ButtonBar(
alignment: MainAxisAlignment.center,
children: [
Text(
i == 1
? counterProvider.counter1.toString()
: counterProvider.counter2.toString(),
style: Theme.of(context).textTheme.headline3,
),
FlatButton(
color: Colors.blue,
onPressed: () {
/// UPDATE DATA IN THE PROVIDER. BECAUSE YOU're USING THE SETTER HERE,
/// THE LISTENERS WILL BE NOTIFIED AND UPDATE ACCORDINGLY
/// you can do this in any other file anywhere in the Widget tree, as long as
/// it it beneath the main.dart file where you defined the MultiProvider
i == 1
? counterProvider.counter1 += 1
: counterProvider.counter2 += 1;
setState(() {});
},
child: Text("Click"),
),
],
);
}
}
If you want, you can change the implementation a bit. If you have multiple counters, for multiple widgets, then just create more variables in the CounterProvider.dart file with separate setters and getters for each counter. Then, to display/update them properly, just use a switch case inside the _showCounterButton() method and inside the onPressed: (){ switch case here, before setState((){}); }.
Hope this helps and gives you a better understanding of how Provider works.
I am very new to Flutter but coming from an Angular background here. Say I want to build a BaseFrame for my app, and depending on the routing, I want to change the INSIDE of that BaseFrame. I don't understand how this would work?
In Angular it would be something like:
'/page-one': PageOne(),
'/page-two': PageTwo(), children: [
'/part-1': Part1(),
'/part-2': Part2(),
];
And in this case, if you navigate to /page-two/part-1 then it would load PageTwo(), and it would load Part1() wherever you specified <app-route></app-route> on PageTwo(). I don't understand how one would do this in Flutter because in Flutter it seems like you can only ever have a single flat route in your main.dart (in your MaterialApp builder)
The best thing I can think of is to have a variable on PageTwo(), and have a type of switch statement:
switch (subPage) {
case '/part-1':
return Part1();
case '/part-2':
return Part2();
}
But this seems like a crappy solution. You also now have the problem of fixing animations etc yourself (because the MaterialApp won't help you here automatically).
Here is something about this but this seems super complicated for a noob. Is this really the right/only way to do this:
Nesting routes with flutter
You can do something like that using this package qlevar_router
A full example of your case
import 'package:flutter/material.dart';
import 'package:qlevar_router/qlevar_router.dart';
void main() {
runApp(MyApp());
}
class AppRoutes {
final routes = <QRouteBase>[
QRoute(
path: '/page-one',
page: (c) => PageOne(),
),
QRoute(
path: '/page-two',
page: (c) => PageTwo(c),
initRoute: '/part-1',
children: [
QRoute(
path: '/part-1',
page: (c) => Part1(),
),
QRoute(
path: '/part-2',
page: (c) => Part2(),
)
])
];
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp.router(
routerDelegate: QR.router(AppRoutes().routes, initRoute: '/page-one'),
routeInformationParser: QR.routeParser(),
);
}
}
class PageOne extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(children: [
Text('This is page one'),
TextButton(
onPressed: () => QR.to('/page-two/part-1'),
child: Text('To Part 1')),
TextButton(
onPressed: () => QR.to('/page-two/part-2'),
child: Text('To Part 2')),
]),
),
);
}
}
class PageTwo extends StatelessWidget {
final QRouteChild child;
PageTwo(this.child);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(children: [
Text('This is page one'),
TextButton(onPressed: () => QR.back(), child: Text('Back')),
TextButton(
onPressed: () => QR.to('/page-two/part-1'),
child: Text('To Part 1')),
TextButton(
onPressed: () => QR.to('/page-two/part-2'),
child: Text('To Part 2')),
SizedBox(
width: 500,
height: 500,
child: child.childRouter,
)
]),
),
);
}
}
class Part1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(child: Text('This is part 1'));
}
}
class Part2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(child: Text('This is part 2'));
}
}
Note that page-tow doesn't change at all when you navigate between the children part-1 and part-2
You could just use something like a TabBar/TabBarView to have other pages show up within the pages you're on. Which then you could give an integer as a parameter of your PageTwo class which you could use that integer to select which page you initially navigate to. This is a fairly straightforward way to do what I think you want but more limited since you won't have different navigators within each of those initial pages. Unless the Parts pages are meant to have their own navigation. If you want to have separate navigators for screens you can do this. That's how the CupertinoTabbedScaffold works (though with CupertinoApps). Which may be something to look into.
I am learning app development on Flutter and can't get my Slider to work within the AlertDialog. It won't change it's value.
I did search the problem and came across this post on StackOverFlow:
Flutter - Why slider doesn't update in AlertDialog?
I read it and have kind of understood it. The accepted answer says that:
The problem is, dialogs are not built inside build method. They are on a different widget tree. So when the dialog creator updates, the dialog won't.
However I am not able to understand how exactly does it have to be implemented as not enough background code is provided.
This is what my current implementation looks like:
double _fontSize = 1.0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(qt.title),
actions: <Widget>[
IconButton(
icon: Icon(Icons.format_size),
onPressed: () {
getFontSize(context);
},
),
],
),
body: ListView.builder(
padding: EdgeInsets.symmetric(vertical: 15.0),
itemCount: 3,
itemBuilder: (context, index) {
if (index == 0) {
return _getListTile(qt.scripture, qt.reading);
} else if (index == 1) {
return _getListTile('Reflection:', qt.reflection);
} else {
return _getListTile('Prayer:', qt.prayer);
}
})
);
}
void getFontSize(BuildContext context) {
showDialog(context: context,builder: (context){
return AlertDialog(
title: Text("Font Size"),
content: Slider(
value: _fontSize,
min: 0,
max: 100,
divisions: 5,
onChanged: (value){
setState(() {
_fontSize = value;
});
},
),
actions: <Widget>[
RaisedButton(
child: Text("Done"),
onPressed: (){},
)
],
);
});
}
Widget parseLargeText(String text) {...}
Widget _getListTile(String title, String subtitle) {...}
I understand that I will need to make use of async and await and Future. But I am not able to understand how exactly. I've spent more than an hour on this problem and can't any more. Please forgive me if this question is stupid and noobish. But trust me, I tried my best.
Here is a minimal runnable example. Key points:
The dialog is a stateful widget that stores the current value in its State. This is important because dialogs are technically separate "pages" on your app, inserted higher up in the hierarchy
Navigator.pop(...) to close the dialog and return the result
Usage of async/await
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
double _fontSize = 20.0;
void _showFontSizePickerDialog() async {
// <-- note the async keyword here
// this will contain the result from Navigator.pop(context, result)
final selectedFontSize = await showDialog<double>(
context: context,
builder: (context) => FontSizePickerDialog(initialFontSize: _fontSize),
);
// execution of this code continues when the dialog was closed (popped)
// note that the result can also be null, so check it
// (back button or pressed outside of the dialog)
if (selectedFontSize != null) {
setState(() {
_fontSize = selectedFontSize;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Font Size: ${_fontSize}'),
RaisedButton(
onPressed: _showFontSizePickerDialog,
child: Text('Select Font Size'),
)
],
),
),
);
}
}
// move the dialog into it's own stateful widget.
// It's completely independent from your page
// this is good practice
class FontSizePickerDialog extends StatefulWidget {
/// initial selection for the slider
final double initialFontSize;
const FontSizePickerDialog({Key key, this.initialFontSize}) : super(key: key);
#override
_FontSizePickerDialogState createState() => _FontSizePickerDialogState();
}
class _FontSizePickerDialogState extends State<FontSizePickerDialog> {
/// current selection of the slider
double _fontSize;
#override
void initState() {
super.initState();
_fontSize = widget.initialFontSize;
}
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('Font Size'),
content: Container(
child: Slider(
value: _fontSize,
min: 10,
max: 100,
divisions: 9,
onChanged: (value) {
setState(() {
_fontSize = value;
});
},
),
),
actions: <Widget>[
FlatButton(
onPressed: () {
// Use the second argument of Navigator.pop(...) to pass
// back a result to the page that opened the dialog
Navigator.pop(context, _fontSize);
},
child: Text('DONE'),
)
],
);
}
}
You just need to warp the AlertDialog() with a StatefulBuilder()