How to use onTap or onPressed in PopupMenuItem - flutter

How can we implement use of onTap or onPressed in PopupMenuItem
Here is my code:
actions: <Widget>[
PopupMenuButton(
icon: Icon(Icons.settings),
itemBuilder: (context) => [
PopupMenuItem(
child: Text("Settings"),
),
PopupMenuItem(
child: Text("Flutter.io"),
),
PopupMenuItem(
child: Text("Google.com"),
),
],
),
]
I want to navigate to SettingPage() on tapping or clicking Settings PopupMenuItem.
I am getting this error even after following a solution mentioned below and even after importing dart:js
Error: Not found: 'dart:js'
import 'dart:js';
Here are my dependencies:
import 'package:bfdi_app/Profile/editProfile.dart';
import 'package:bfdi_app/models/user.dart';
import 'package:bfdi_app/services/collection.dart';
import 'package:bfdi_app/settings.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'dart:js';

Just add this to your PopupMenuButton:
onSelected: (result) {
if (result == 0) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SettingPage()),
);
}
},
And change your setting button to:
PopupMenuItem(
child: Text("Settings"),
value: 0,
),

There is a property called onSelected, you should use it, it handles onTap event.
PopupMenuButton(
icon: Icon(Icons.settings),
onSelected: (newValue) { // add this property
setState(() {
_value = newValue; // it gives the value which is selected
});
},
itemBuilder: (context) => [
PopupMenuItem(
child: Text("Settings"),
value: 0,
),
PopupMenuItem(
child: Text("Flutter.io"),
value: 1,
),
PopupMenuItem(
child: Text("Google.com"),
value: 2,
),
],
)

I faced similar issues while navigating the screen using pop up menu button and I solve the issues by putting this method inside the onTap callback of PopupMenuItem:
onTap: (){
WidgetsBinding.instance!.addPostFrameCallback((_) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return ScreenName();
},
),
);
});
}

There is now an onTap() for PopupMenuItem.
PopupMenuButton(
itemBuilder: (context) => [
PopupMenuItem(
child: Text("tap me"),
onTap: () => print("TAP"),
)
],
),

-Edited based on comment-
That's it :
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var items = [{'name':'Settings','value':0}, {'name':'Flutter.io','value':1}, {'name':'Google.com',,'value':2}];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: PopupMenuButton(
onSelected: (x) {
if(x==0){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SettingPage()), );}
},
icon: Icon(Icons.settings),
itemBuilder: (context) => items
.map<PopupMenuItem>((element) => PopupMenuItem(
child: Text(element['name]),
value: element['value'],
))
.toList()),
));
}
}

Callable value.
PopupMenuButton(
icon: Icon(Icons.settings),
onSelected: (value) {
value();
},
itemBuilder: (context) => [
PopupMenuItem(
child: Text('Settings'),
value: () {
debugPrint('open Settings');
},
),
PopupMenuItem(
child: Text('Flutter.io'),
value: () {
debugPrint('goto Flutter.io');
},
),
],
)

Use inheritance when showing popup with showMenu(...)
class PopupItem<T> extends PopupMenuItem<T> {
final Function() onTap;
PopupItem({this.onTap, Widget child, Key key})
: super(child: child, key: key);
#override
_PopupItemState<T> createState() => _PopupItemState<T>();
}
class _PopupItemState<T> extends PopupMenuItemState<T, PopupItem<T>> {
#override
void handleTap() {
widget.onTap?.call();
super.handleTap();
}
}
Usage:
Widget _itemDelete() => PopupItem(
onTap: () {
// Handle tap here
},
child: Text(...)
);

onTap: (){
WidgetsBinding.instance!.addPostFrameCallback((_) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context)=>Page())):
},
),
);
});
}

PopupMenuButton (
padding: EdgeInsets.zero,
icon:const Icon(Icons.keyboard_arrow_down_outlined,color: Color.fromRGBO(34, 178, 232, 1)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0))
),
onSelected: (x){
if (x=="Edit User") {
Get.to(EditUserPage());
}
else if(x=="Login History"){
Get.to(EditUserPage());
}
else if(x=="Change Password"){
Get.to(EditUserPage());
}
else if(x=="Deactivate User"){
Get.to(EditUserPage());
}
},
itemBuilder: (BuildContext context) => <PopupMenuEntry >[
PopupMenuItem (
onTap: (){},
value:"Edit User",
child: Row(
children: [
svgWedget(url:"assets/svg/edit.svg"),
SizedBox(width: 10,),
Text("Edit User"),
],
),
),
PopupMenuItem (
onTap: ( )async{},
value:"Login History",
child: Row(
children: [
svgWedget(url:"assets/svg/calendar.svg"),
SizedBox(width: 10,),
Text("Login History"),
],
),
),
PopupMenuItem (
onTap: ()async{},
value:"Change Password",
child: Row(
children: [
SvgPicture.asset("assets/svg/lock.svg",color: Color.fromRGBO(195, 172, 255, 1),),
SizedBox(width: 10,),
Text("Change Password"),
],
),
),
PopupMenuItem (
onTap: ()async{},
value:"Deactivate User",
child: Row(
children: [
svgWedget(url:"assets/svg/trash.svg"),
SizedBox(width: 10,),
Text("Deactivate User"),
],
),
),
]
),

Related

Different button will take me through different pages in Flutter

I'm working on a flutter project. I have a multiple button (Container wrap in Inkwell) using only one button in code and in List. I have tried to handle multiple buttons work in a List. But there is an error showing on the onTap function.
import 'package:flutter/material.dart';
import '../Ambulance/AmbulanceHome.dart';
import '../Blood Bank/BloodHome.dart';
import '../CreateCase/CaseHome.dart';
import '../Doctor Appoinment/HomeScreen.dart';
class BottomHomePage extends StatelessWidget {
List<FeaturesList> featuresList = [
FeaturesList('assets/BloodBank.jpg', 'Blood Bank', (context) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) {
return HomeScreen();
},
),
);
}),
FeaturesList('assets/BloodBank.jpg', 'Doctor Appoinment', (context) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) {
return HomeScreen();
},
),
);
}),
FeaturesList('assets/BloodBank.jpg', 'Create a Case', (context) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) {
return HomeScreen();
},
),
);
}),
FeaturesList('assets/BloodBank.jpg', 'Ambulance', (context) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) {
return HomeScreen();
},
),
);
}),
];
// void selectFeatures(BuildContext context) {
// Navigator.of(context).push(
// MaterialPageRoute(
// builder: (_) {
// return HomeScreen();
// },
// ),
// );
// }
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFD9E4EE),
body: Padding(
padding: const EdgeInsets.all(10),
child: Column(
children: [
Container(
width: double.infinity,
height: 150,
decoration: BoxDecoration(
color: Theme.of(context).primaryColor.withOpacity(0.8),
borderRadius: BorderRadius.circular(20),
),
child: const Text(
'Fade-in logo of our project added here',
textAlign: TextAlign.center,
),
),
const SizedBox(height: 20),
Container(
alignment: AlignmentDirectional.topStart,
child: Text(
"Features",
style: Theme.of(context).textTheme.headline6,
),
),
const SizedBox(height: 20),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(
featuresList.length,
(index) {
return Column(
children: [
InkWell(
onTap: () => featuresList.callback?.call(context),
child: Container(
width: 150,
height: 150,
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(50),
// color: Theme.of(context)
// .colorScheme
// .primaryContainer
// .withOpacity(0.4),
),
child: Image.asset(featuresList[index].icon),
),
),
Text(featuresList[index].name),
],
);
},
),
),
),
],
),
),
);
}
}
class FeaturesList {
final String icon;
final String name;
final Function(BuildContext) callback;
FeaturesList(
this.icon,
this.name,
this.callback,
);
}
I have tried to use the navigation route in the List, but when I try to call the function called callback (which is defined in Features List class).
The error is on the line:
onTap: () => featuresList.callback?.call(context),
redline on the callback
Error message:
The getter 'callback' isn't defined for the type 'List'.
Try importing the library that defines 'callback', correcting the name to the name of an existing getter, or defining a getter or field named 'callback'.
featuresList is a List, so it does not have callback property. You should use
onTap: () => featuresList[index].callback?.call(context)

Flutter Alert Dialog doesn't work/displaying

So I am facing this problem that my alert Dialog isn't displaying. I had tried every possible solution and searching here and there but nothing works. When I click on the edit button from the pop up menu nothing is displayed everything remains the same.
Calling alert Dialog
trailing: PopupMenuButton(
icon: Icon(Icons.more_vert),
itemBuilder: (context)=>[
PopupMenuItem(
value:1,
onTap: (){
//debugPrint('popup');
Navigator.pop(context);
_showMyDialog();
},
child: ListTile(
leading: Icon(Icons.edit),
title: Text('Edit'),
)),
PopupMenuItem(
value:1,
// onTap: (){
// Navigator.pop(context);
// showDialogBox();
// },
child: ListTile(
leading: Icon(Icons.delete),
title: Text('Delete'),
)),
]),
Alert Dialog Code
Future<void> showDialogBox(String title)async{
editController.text=title;
debugPrint('dialog');
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context){
debugPrint('alert');
return AlertDialog(
title: Text('Update'),
content: Container(
child: TextFormField(
controller: editController,
),
),
actions: [
TextButton(onPressed: (){
Navigator.pop(context);
}, child: Text('Update')),
TextButton(onPressed: (){
Navigator.pop(context);
}, child: Text('Cancel')),
],
);
}
);
}
Complete Class Code
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/ui/firebase_animated_list.dart';
import 'package:firebase_tutorial/utils/routes/routes_names.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:firebase_database/firebase_database.dart';
import '../../utils/utils.dart';
class PostScreen extends StatefulWidget {
const PostScreen({Key? key}) : super(key: key);
#override
State<PostScreen> createState() => _PostScreenState();
}
class _PostScreenState extends State<PostScreen> {
final ref=FirebaseDatabase.instance.ref('Post');
FirebaseAuth _auth=FirebaseAuth.instance;
final searchController=TextEditingController();
final editController=TextEditingController();
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: ()async{
SystemNavigator.pop();
return true;
},
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text('Post Screen'),
actions: [
GestureDetector(
onTap: (){
_auth.signOut().then((value){
Navigator.pushNamed(context, RoutesNames.loginScreen);
}).onError((error, stackTrace){
Utils().toastMessage(error.toString());
});
},
child: Icon(Icons.logout_outlined)),
SizedBox(width: 10,),
],
),
floatingActionButton: FloatingActionButton(
onPressed:(){
Navigator.pushNamed(context, RoutesNames.newPost);
},
child: Icon(Icons.add),),
body: Column(
children: [
// Expanded(
// child:FirebaseAnimatedList(
// query: ref,
// itemBuilder: (context,snapshot,animation,index){
// return ListTile(
// title: Text(snapshot.child('post').value.toString()),
// );
// }
// ),
// ),
Padding(
padding: const EdgeInsets.all(10.0),
child: TextFormField(
onChanged: (String value){
setState(() {
});
},
controller: searchController,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Search",
),
),
),
Expanded(child: StreamBuilder(
stream: ref.onValue,
builder: (context,AsyncSnapshot<DatabaseEvent> snapshot){
if(!snapshot.hasData){
return CircularProgressIndicator();
}
else{
return ListView.builder(
itemCount: snapshot.data!.snapshot.children.length,
itemBuilder: (context,index){
Map<dynamic,dynamic> map=snapshot.data!.snapshot.value as dynamic;
List<dynamic> list=[];
list.clear();
list=map.values.toList();
final title=list[index]['post'].toString();
if(searchController.text.isEmpty){
return ListTile(
title: Text(list[index]['post']),
subtitle: Text(list[index]['id'].toString()),
trailing: PopupMenuButton(
icon: Icon(Icons.more_vert),
itemBuilder: (context)=>[
PopupMenuItem(
value:1,
onTap: (){
//debugPrint('popup');
Navigator.pop(context);
_showMyDialog();
},
child: ListTile(
leading: Icon(Icons.edit),
title: Text('Edit'),
)),
PopupMenuItem(
value:1,
// onTap: (){
// Navigator.pop(context);
// showDialogBox();
// },
child: ListTile(
leading: Icon(Icons.delete),
title: Text('Delete'),
)),
]),
);
}
else if(title.toLowerCase().contains(searchController.text.toLowerCase())){
return ListTile(
title: Text(list[index]['post']),
subtitle: Text(list[index]['id'].toString()),
);
}
else{
return Container();
}
});
}
}))
],
),
),
);
}
Future<void> showDialogBox(String title)async{
editController.text=title;
debugPrint('dialog');
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context){
debugPrint('alert');
return AlertDialog(
title: Text('Update'),
content: Container(
child: TextFormField(
controller: editController,
),
),
actions: [
TextButton(onPressed: (){
Navigator.pop(context);
}, child: Text('Update')),
TextButton(onPressed: (){
Navigator.pop(context);
}, child: Text('Cancel')),
],
);
}
);
}
}
try adding a delay before calling showDialog like this:
await Future.delayed(const Duration(milliseconds: 10));
Your dialog isnt displayed because when you select a menu item the pop() method is automatically called to close the popup menu; so if you open a dialog immediately, the dialog will get automatically popped.
hope this fixes your issue

Flutter/Dart - Text value not showing correctly

I am trying to create a shopping cart using provider and display the number of items currently in the cart on my homepage. When I create my cart icon with a text widget overlaid, the value being shown does not reflect the number of items in the cart.
Here is my code:
class OurShoppingBasketIcon extends StatelessWidget {
const OurShoppingBasketIcon({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.center,
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ShoppingBasketScreen()),
);
},
child: Stack(
children: <Widget>[
new Icon(
Icons.shopping_cart_outlined,
color: Colors.white,
),
new Positioned(
right: 0,
child: new Container(
padding: EdgeInsets.all(1),
decoration: new BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(6),
),
constraints: BoxConstraints(
minWidth: 12,
minHeight: 12,
),
child: Text(
context.read<ShoppingBasket>().items.length.toString(),
style: new TextStyle(
color: Colors.white,
fontSize: 8,
),
textAlign: TextAlign.center,
),
),
)
],
),
),
);
}
}
This is where the icon is implemented:
class OurHomePage extends StatefulWidget {
#override
_OurHomePageState createState() => _OurHomePageState();
}
class _OurHomePageState extends State<OurHomePage> {
#override
Widget build(BuildContext context) {
return Consumer<OurUser>(
builder: (_, user, __) {
return ChangeNotifierProvider<SignInViewModel>(
create: (_) => SignInViewModel(context.read),
builder: (_, child) {
return Scaffold(
appBar: AppBar(
title: Text("My app"),
actions: [
OurShoppingBasketIcon(),
IconButton(
icon: Icon(
Icons.logout,
color: Colors.white,
),
onPressed: () {
context.read<FirebaseAuthService>().signOut();
},
),
],
),
);
},
);
},
);
}
}
There are 2 items in the cart as of writing this:
But the icon on the homepage does not change:
Here is my main.dart:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MultiProvider(
providers: [
Provider(
create: (_) => FirebaseAuthService(),
),
StreamProvider<OurUser>(
create: (context) =>
context.read<FirebaseAuthService>().onAuthStateChanged),
ChangeNotifierProvider.value(
value: ShoppingBasket(),
),
],
child: MaterialApp(theme: OurTheme().buildTheme(), home: OurHomePage()),
),
);
}
perhaps if you watch for the value it will be updated dynamically:
context.watch<ShoppingBasket>().items.length.toString(), //<-- watch instead of read
The OurHomePage needs to be wrapped in the Provider<ShoppingBasket>.
return Provider<ShoppingBasket>(
create: (context) => ShoppingBasket(),
child: Consumer<OurUser>(
builder: (_, user, __) {
return ChangeNotifierProvider<SignInViewModel>(
create: (_) => SignInViewModel(context.read),
builder: (_, child) {
return Scaffold(
appBar: AppBar(
title: Text("My app"),
actions: [
OurShoppingBasketIcon(),
IconButton(
icon: Icon(
Icons.logout,
color: Colors.white,
),
onPressed: () {
context.read<FirebaseAuthService>().signOut();
},
),
],
),
),
);
},
);
I forgot to NotifyListeners() in my Change Notifier class:
class ShoppingBasket extends ChangeNotifier {
Map<String, SingleBasketItem> _items = {};
Map<String, SingleBasketItem> get items {
return {..._items};
}
void addItem(String id) {
_items.putIfAbsent(
id,
() => SingleBasketItem(id),
);
notifyListeners(); //HERE
}

Why is navigator.push not working in my flutter web app?

I am building a flutter web app. I have a widget which I control with cubit, depending on if the user is logged in or not. If the user is logged in, I displa his profile picture as a dropdown button, and if he selects option A i want to push him to a different page. For some reason, the Navigator isn't doing anything. This is the code for my dropdown button:
MouseRegion(
cursor: SystemMouseCursors.click,
child: DropdownButton(
underline: SizedBox.shrink(),
icon: SizedBox.shrink(),
hint: CircleAvatar(
backgroundImage: NetworkImage(user.photoURL.toString()),
),
onChanged: (value) {
setState(() {});
},
items: [
DropdownMenuItem(
onTap: () {
print('tap');
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => UserPage(user)));
},
value: dropdownValue,
child: Row(
children: [Text('a'), Icon(Icons.ac_unit)],
)),
DropdownMenuItem(
value: menuItem,
child: Row(
children: [Text('b'), Icon(Icons.access_alarms)],
))
],
),
)
This is the code for the page I want to push to:
import 'package:ez_user_side/cubit/cubit/sign_in_cubit.dart';
import 'package:ez_user_side/pages/page_components/Menu.dart';
import 'package:ez_user_side/pages/page_components/colors.dart';
import 'package:ez_user_side/pages/page_components/right_side.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:ez_user_side/pages/page_components/user_middle.dart';
class UserPage extends StatefulWidget {
final User user;
UserPage(this.user);
#override
_UserPageState createState() => _UserPageState();
}
class _UserPageState extends State<UserPage> {
#override
Widget build(BuildContext context) {
return Container(
child: SizedBox(
width: MediaQuery.of(context).size.width / 100 * 13.46,
child: Row(
children: [
Menu(),
Container(
color: Colors.red,
),
AccountTab()
],
)),
);
}
}
Thank you for your help!
You need to keep track of the selected value of the DropdownButton. You can do that with a simple variable in a StatefulWidget. Here the variable value is used to determine which item is selected. Whenever some DropdownMenuItem is selected you update the variable with the selected DropdownMenuItem's value.
After that, all you need is to react changes in the DropdownButton's onChanged:(value) callback to redirect to some other page or do something else.
Here's the code:
import 'package:flutter/material.dart';
class Solution extends StatefulWidget {
#override
_SolutionState createState() => _SolutionState();
}
class _SolutionState extends State<Solution> {
String value = "home";
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
child: DropdownButton<String>(
value: value,
underline: SizedBox.shrink(),
icon: SizedBox.shrink(),
hint: CircleAvatar(
backgroundImage: NetworkImage(user.photoURL.toString()),
),
onChanged: (value) async{
setState(() {
if(value == 'home') {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserPage())
);
}
else {
//DO SOMETHING
}
});
},
items: [
DropdownMenuItem(
onTap: () {
setState(() {
value = 'home';
});
print('tapped home');
},
value: 'home',
child: Row(
children: [Text('a'), Icon(Icons.ac_unit)],
)
),
DropdownMenuItem(
onTap: () {
setState(() {
value = 'alarm';
});
print('tapped alarm');
},
value: 'alarm',
child: Row(
children: [Text('b'), Icon(Icons.access_alarms)],
)
)
],
),
),
),
);
}
}

Why isn't Navigator.pop() refreshing data?

Hi guys I'm trying to build an app with flutter, so I have two screens HomeScreen() and RoutineScreen(). The first one is a Scaffold and in the body has a child Widget (a ListView called RoutinesWidget()) with all the routines. And the second one is to create a routine. The thing is, that when I create the routine, I use a button to pop to the HomeScreen() but it doesn't refresh the ListView (I'm guessing that it's because when I use Navigator.pop() it refreshes the Scaffold but not the child Widget maybe?)
HomeScreen() code here:
import 'package:flutter/material.dart';
import 'package:workout_time/constants.dart';
import 'package:workout_time/Widgets/routines_widget.dart';
import 'package:workout_time/Widgets/statistics_widget.dart';
import 'package:workout_time/Screens/settings_screen.dart';
import 'package:workout_time/Screens/routine_screen.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int _selectedIndex = 0;
List<Widget> _views = [
RoutinesWidget(),
StatisticsWidget(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kThirdColor,
appBar: AppBar(
leading: Icon(Icons.adb),
title: Text("Workout Time"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.settings),
onPressed: () => Navigator.push(context,
MaterialPageRoute(builder: (context) => SettingsScreen()))),
],
),
body: _views[_selectedIndex],
floatingActionButton: (_selectedIndex == 1)
? null
: FloatingActionButton(
onPressed: () async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RoutineScreen(null)));
setState(() {});
},
child: Icon(
Icons.add,
color: kSecondColor,
size: 30.0,
),
elevation: 15.0,
),
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
bottomItems(Icon(Icons.fitness_center_rounded), "Routines"),
bottomItems(Icon(Icons.leaderboard_rounded), "Statistics"),
],
currentIndex: _selectedIndex,
onTap: (int index) => setState(() => _selectedIndex = index),
),
);
}
}
BottomNavigationBarItem bottomItems(Icon icon, String label) {
return BottomNavigationBarItem(
icon: icon,
label: label,
);
}
RoutinesWidget() code here:
import 'package:flutter/material.dart';
import 'package:workout_time/Services/db_crud_service.dart';
import 'package:workout_time/Screens/routine_screen.dart';
import 'package:workout_time/constants.dart';
import 'package:workout_time/Models/routine_model.dart';
class RoutinesWidget extends StatefulWidget {
#override
_RoutinesWidgetState createState() => _RoutinesWidgetState();
}
class _RoutinesWidgetState extends State<RoutinesWidget> {
DBCRUDService helper;
#override
void initState() {
super.initState();
helper = DBCRUDService();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: helper.getRoutines(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
Routine routine = Routine.fromMap(snapshot.data[index]);
return Card(
margin: EdgeInsets.all(1.0),
child: ListTile(
leading: CircleAvatar(
child: Text(
routine.name[0],
style: TextStyle(
color: kThirdOppositeColor,
fontWeight: FontWeight.bold),
),
backgroundColor: kAccentColor,
),
title: Text(routine.name),
subtitle: Text(routine.exercises.join(",")),
trailing: IconButton(
icon: Icon(Icons.delete_rounded),
color: Colors.redAccent,
onPressed: () {
setState(() {
helper.deleteRoutine(routine.id);
});
},
),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RoutineScreen(routine))),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0)),
color: kSecondColor,
);
},
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
);
}
}
RoutineScreen() code here:
import 'package:flutter/material.dart';
import 'package:workout_time/Models/routine_model.dart';
import 'package:workout_time/Widgets/type_card_widget.dart';
import 'package:workout_time/constants.dart';
import 'package:workout_time/Services/db_crud_service.dart';
class RoutineScreen extends StatefulWidget {
final Routine _routine;
RoutineScreen(this._routine);
#override
_RoutineScreenState createState() => _RoutineScreenState();
}
class _RoutineScreenState extends State<RoutineScreen> {
DBCRUDService helper;
final _nameController = TextEditingController();
final _descriptionController = TextEditingController();
bool _type = true;
int _cycles = 1;
int _restBetweenExercises = 15;
int _restBetweenCycles = 60;
#override
void initState() {
super.initState();
helper = DBCRUDService();
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context),
),
title: widget._routine != null
? Text(widget._routine.name)
: Text("Create your routine"),
actions: [
IconButton(
icon: Icon(Icons.done_rounded),
onPressed: createRoutine,
)
],
bottom: TabBar(
tabs: [
Tab(
text: "Configuration",
),
Tab(
text: "Exercises",
),
],
),
),
body: TabBarView(children: [
//_routine == null ? ConfigurationNewRoutine() : Text("WIDGET N° 1"),
ListView(
children: [
Container(
padding: EdgeInsets.all(15.0),
child: Row(
children: [
Text(
"Name:",
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
SizedBox(
width: 40.0,
),
Expanded(
child: TextField(
textAlign: TextAlign.center,
controller: _nameController,
),
),
],
),
),
SizedBox(
height: 20.0,
),
Card(
margin: EdgeInsets.all(15.0),
color: kSecondColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: Container(
padding: EdgeInsets.all(15.0),
child: Column(
children: [
Text(
"Type",
style: TextStyle(fontSize: 25.0),
),
Row(
children: [
Expanded(
child: TypeCard(
Icons.double_arrow_rounded,
_type == true ? kFirstColor : kThirdColor,
() => setState(() => _type = true),
"Straight set",
),
),
Expanded(
child: TypeCard(
Icons.replay_rounded,
_type == false ? kFirstColor : kThirdColor,
() => setState(() => _type = false),
"Cycle",
),
),
],
),
],
),
),
),
SizedBox(
height: 20.0,
),
Container(
padding: EdgeInsets.all(15.0),
child: Row(
children: [
Text(
"N° cycles:",
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
SizedBox(
width: 40.0,
),
Expanded(
child: Text("Hello"),
),
],
),
),
SizedBox(
height: 20.0,
),
],
),
Text("WIDGET N° 2"),
]),
),
);
}
void createRoutine() {
List<String> _exercises = ["1", "2"];
List<String> _types = ["t", "r"];
List<String> _quantities = ["30", "20"];
Routine routine = Routine({
'name': _nameController.text,
'description': "_description",
'type': _type.toString(),
'cycles': 1,
'numberExercises': 2,
'restBetweenExercises': 15,
'restBetweenCycles': 60,
'exercises': _exercises,
'types': _types,
'quantities': _quantities,
});
setState(() {
helper.createRoutine(routine);
Navigator.pop(context);
});
}
}
Any idea what can I do to make it work? Thank you
Make it simple
use Navigator.pop() twice
so that the current class and old class in also removed
from the stack
and then use Navigator.push()
When you push a new Route, the old one still stays in the stack. The new route just overlaps the old one and forms like a layer above the old one. Then when you pop the new route, it will just remove the layer(new route) and the old route will be displayed as it was before.
Now you must be aware the Navigator.push() is an asynchronous method and returns a Future. How it works is basically when you perform a Navigator.push(), it will push the new route and will wait for it to be popped out. Then when the new route is popped, it returns a value to the old one and that when the future callback will be executed.
Hence the solution you are looking for is add a future callback like this after your Navigator.push() :
Navigator.push(context,
MaterialPageRoute(builder: (context) => SettingsScreen())
).then((value){setState(() {});}); /// A callback which is executed after the new route will be popped. In that callback, you simply call a setState and refresh the page.