Flutter: Passing Data between Classes Resets the Other Value - flutter

I try to pass sRegions value from Regions class to the AddAd class
and passing sCategory value from Categories class to AddAd class
but when I click and pass this value , it resets the other value to initial value
I want to the values inside AddAd class to be separated and do not affect each other
Here is AddAd() class:
class AddAd extends StatefulWidget {
final String sRegion;
final String sCategory;
AddAd(this.sRegion,this.sCategory, {Key key}):super(key: key);
#override
_AddAdState createState() => _AddAdState();
}
ListTile(
title: Text("Location"),
subtitle: widget.sRegion == null ? Text("Where exactly") : Text(widget.sRegion),
),
ListTile(
title: Text("Category"),
subtitle: widget.sCategory == null ? Text("Department") : Text(widget.sCategory),
),
and here is how I pass data from Regions class:
child: Text(item),
onPressed: () {
sRegion = item;
Navigator.push(context, new MaterialPageRoute(builder: (context) => AddAd(sRegion, sCategory)));
print(sRegion);
},
and here how I pass data from Categories class:
class _RegionsState extends State<Regions> {
String sRegion;
String sCategory;
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
appBar: AppBar(
title: Text("Location"),
actions: <Widget>[
FlatButton(
child: Text('Cancel', style: TextStyle(color: Colors.white),),
onPressed: (){
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context)=>AddAd(sRegion, sCategory)));
},
)
],
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top:15.0),
child: Container(
child: Column(
children: <Widget>[
for(var item in regions) FlatButton(
child: Text(item), onPressed: (){
sRegion = item;
Navigator.push(context, new MaterialPageRoute(builder: (context) => AddAd(sRegion, sCategory)));
print(sRegion);
},),
Divider(),
],
),
),
),
),
),
);
}
}

Related

How can I make an Expandable Panel editable?

I'm new to flutter and I'm currently working on an app for my school project. At the moment I cannot make my expanded panel editable, could anybody tell me how I can make something editable or if it's not possible tell me an alternativ solution? Here's the code btw.:
class MyItem {
MyItem({this.isExpanded = false, required this.header, required this.body});
bool isExpanded;
final String header;
final String body;
}
class fourthPage extends StatefulWidget {
#override
list createState() => list();
}
class list extends State<fourthPage> {
final List<MyItem> _items = <MyItem>[
MyItem(
header: "header1",
body: "text1"),
MyItem(
header: "header2",
body: "text2"),
MyItem(
header: "header3",
body: "text3"),
];
#override
Widget build(BuildContext context) {
return _buildPage();
}
Widget _buildPage() {
return SafeArea(
top: true,
child: Scaffold(
appBar: AppBar(
title: const Text("page4"),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text(
'Assignments:',
style: TextStyle(fontSize: 35),
),
ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
_items[index].isExpanded = !_items[index].isExpanded;
});
},
children: _items.map((MyItem item) {
return ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
return Text(item.header);
},
isExpanded: item.isExpanded,
body: Container(child: Text(item.body)));
}).toList(),
),
ElevatedButton(
child: const Text('Page 1'),
onPressed: () {
Navigator.of(this.context).push(MaterialPageRoute(
builder: (context) => const FirstPage()));
},
),
ElevatedButton(
child: const Text('Page 2'),
onPressed: () {
Navigator.of(this.context).push(MaterialPageRoute(
builder: (context) => const SecondPage()));
},
),
ElevatedButton(
child: const Text('Page 3'),
onPressed: () {
Navigator.of(this.context).push(MaterialPageRoute(
builder: (context) => const ThirdPage()));
},
),
],
),
),
));
}
}
I have thought about using onclick in combination with a function but I just can't do it If anybody could help me I would really appreciate it

Flutter save page content after routing

i have a favorite icon that exist on page (A) that changes it's color whenever i click, the problem is when i move to another page and go back to page (A) icon goes to regular color, it doesn't save the changes on page , is there a way to keep the changes even after moving from page to page ?
Thanks.
If you push and pop back, then you can find the saved state from the route. How-ever if you pop back, current page state will be removed from stack. To handle this, you need to use state-management property. You can use StateProvider in this case, or the thing that suit for your project.
Demo:
class PageA extends StatefulWidget {
PageA({Key? key}) : super(key: key);
#override
_PageAState createState() => _PageAState();
}
class _PageAState extends State<PageA> {
Color color = Colors.deepOrange;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("PAge A"),
),
body: Center(
child: Column(
children: [
Container(
width: 400,
height: 400,
color: color,
alignment: Alignment.center,
child: Text("init Color:deepOrange "),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => PageB(),
));
},
child: Text("Move to B"),
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
color = Colors.deepPurple;
});
},
),
);
}
}
class PageB extends StatefulWidget {
const PageB({Key? key}) : super(key: key);
#override
_PageBState createState() => _PageBState();
}
class _PageBState extends State<PageB> {
Color color = Colors.greenAccent;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("PAge B"),
),
body: Center(
child: Column(
children: [
Container(
width: 400,
height: 400,
color: color,
alignment: Alignment.center,
child: Text("init Color:greenAccent "),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => PageC(),
),
);
},
child: Text("Move to C"),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text("Move Back to A, you can find last saved state"),
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
color = Colors.black;
});
},
),
);
}
}
class PageC extends StatefulWidget {
PageC({Key? key}) : super(key: key);
#override
_PageCState createState() => _PageCState();
}
class _PageCState extends State<PageC> {
Color color = Colors.amberAccent;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("PAge C"),
),
body: Center(
child: Column(
children: [
Container(
width: 400,
height: 400,
color: color,
child: Center(child: Text("init Color:amberAccent ")),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => PageB(),
),
);
},
child: Text("Push to B, will assing new state on route"),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
"pop to B\n if you pop then current state of Page B will be visible"),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
color = Colors.pink;
});
},
),
);
}
}

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.

Flutter - Checkbox animation doesn't show

The value is effectively changing when clicking but the animation doesn't show :
Here's my code :
var editGender = Padding(
padding: const EdgeInsets.only(top: 12.0),
child: Column(
children: <Widget>[
CheckboxListTile(
value: _male,
onChanged: _maleChanged,
title: Text("Male"),
activeColor: Theme.of(context).primaryColor,
),
CheckboxListTile(
value: _female,
onChanged: _femaleChanged,
title: Text("Female"),
activeColor: Theme.of(context).primaryColor,
)
],
),
);
When tapping the edit button :
FlatButton(
onPressed: (){
buildShowRoundedModalBottomSheet(context, title, editGender, option);
},
child: Text('Edit'),
it shows the bottom sheet :
Future buildShowRoundedModalBottomSheet(BuildContext context, String title, Widget content,[String date]) {
return showRoundedModalBottomSheet(
context: context,
radius: 20.0,
builder: (context){
return Padding(
padding: const EdgeInsets.only(top: 20.0, bottom: 20.0, left: 20.0, right: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
title,
style: TextStyle(
fontFamily: 'SamsungSans',
fontSize: 20.0,
),
),
content,
...
I am passing the same context to the widget :/
setState would change the value but it wouldn't rebuild your bottom sheet as it is being called on a onPressed of a FlatButton. You are certainly not invoking that onPressed again but you wouldn't want to do it either.
As I mentioned in the comments a StatefulBuilder would do the job.
A working example
import 'package:flutter/material.dart';
import 'package:rounded_modal/rounded_modal.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
bool value = false;
void _incrementCounter() {
showRoundedModalBottomSheet(
context: context,
builder: (context) {
return StatefulBuilder(builder: (context, setState) {
return Container(
height: 200.0,
child: Checkbox(value: value, onChanged: (val) {
setState(() {
value = val;
});
}),
);
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
As commented by #10101010, you'll have to use a Stateful widget. And In _femaleChanged and _maleChanged, you'll have to use setState(). Example :
void _femaleChanged(bool value) => setState(() => _female = value);

onTap go to next list item (Flutter)

I have a ListView.builder showing a list, when i click on an item it shows details of that item on the next page (FlashcardDetailsPage).
I'd like to show the next list item when i tap the IconButton in the class FlashcardDetailsPage. So i'd like this button to skip to the next list item. Any ideas?
class FlashcardListView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: allFlashcards.length,
itemBuilder: (context, int index) {
return ListTile(
title: Text(allFlashcards[index].actReg),
subtitle: Text(allFlashcards[index].question),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FlashcardDetailPage(
flashcardToShow: allFlashcards[index]),
),
);
},
);
});
}
}
class FlashcardDetailPage extends StatefulWidget {
final Flashcard flashcardToShow;
FlashcardDetailPage({Key key, #required this.flashcardToShow})
: super(key: key);
#override
_FlashcardDetailPageState createState() => _FlashcardDetailPageState();
}
class _FlashcardDetailPageState extends State<FlashcardDetailPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(242, 242, 242, 1),
appBar: AppBar(
centerTitle: true,
title: Text(widget.flashcardToShow.actReg),
),
body: Column(
children: <Widget>[
Container(
child: Card(
margin: EdgeInsetsDirectional.fromSTEB(20, 20, 20, 0),
child: Center(
child: Text(
widget.flashcardToShow.question,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 30),
),
)),
),
Container(
height: 100.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
icon: Icon(Icons.skip_next),
iconSize: 32.0,
),
],
),
),
],
),
);
}
}
You could just replace the screen with another one showing the next card:
IconButton(
icon: Icon(Icons.skip_next),
iconSiz: 32,
onTap: () {
int currentIndex = allFlashcards.indexOf(widget.flashcardToShow);
if (currentIndex >= allFlashcards.length) return;
var nextFlashcard = allFlashcards[currentIndex + 1];
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (ctx) => FlashDetailsPage(flashcardToShow: nextFlashcard)
));
},
)
Thanks Marcel for the direction! I used your logic for a method. To avoid opening a new page every time I pressed the button, i did this & it's working:
void _skipFlashcard () {
setState(() {
int currentIndex = allFlashcards.indexOf(widget.flashcardToShow);
var nextFlashcard = allFlashcards[currentIndex + 1];
widget.flashcardToShow = nextFlashcard;
});
}
IconButton(
icon: Icon(Icons.skip_next),
iconSize: 32.0,
onPressed: _skipFlashcard,