I'm trying to build a simple shopping cart using bloc. It works fine but the only issue is the cart count doesn't get updated when I add/remove the item. I need to switch to the cartscreen to see the change. I set the counter to cartBloc.cart.length.toString(). What am I missing and am I using bloc correctly?
Cart Bloc
class CartBloc{
List<ProductModel> cart = [];
double totalCartPrice = 0;
final _cartController = StreamController.broadcast();
Stream get getCartStream => _cartController.stream;
void addToCart(ProductModel product) {
cart.add(product);
totalCartPrice = totalCartPrice + double.parse(product.price);
_cartController.sink.add(cart);
}
void removeFromCart(ProductModel product) {
cart.remove(product);
totalCartPrice = totalCartPrice - double.parse(product.price);
_cartController.sink.add(cart);
}
void dispose() {
_cartController?.close();
}
}
final cartBloc = CartBloc();
Main Screen
class _MainScreenState extends State<MainScreen> {
int _currentIndex = 0;
PageController _pageController;
GlobalKey bottomNavigationKey = GlobalKey();
#override
void initState() {
super.initState();
_pageController = PageController();
}
void dispose(){
super.dispose();
_pageController.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor:Color(0xFF20232A),
appBar: PreferredSize(child: Container(),preferredSize: Size.fromHeight(0.0)),
body: SizedBox.expand(
child: PageView(
physics: NeverScrollableScrollPhysics(),
controller: _pageController,
onPageChanged: (index){
setState(() => _currentIndex = index);
},
children: [
Container(
child: ProductScreen()
),
Container(
child: CartScreen()
),
],
)
),
bottomNavigationBar: Container(
child: BottomNavyBar(
mainAxisAlignment: MainAxisAlignment.center,
containerHeight: 56.0,
backgroundColor: Style.Colors.backgroundColor,
selectedIndex: _currentIndex,
onItemSelected: (index){
setState(() => _currentIndex = index);
_pageController.jumpToPage(index);
},
items:<BottomNavyBarItem>[
BottomNavyBarItem(
textAlign: TextAlign.center,
activeColor: Color(0xFF010101),
title: Text(' PRODUCTS',style: TextStyle(
color:Style.Colors.mainColor,fontSize: 13.0
)),
icon: Padding(
padding: EdgeInsets.only(left:5.0),
child: Icon(
SimpleLineIcons.menu,
size:18.0,
color:_currentIndex == 0 ? Style.Colors.mainColor:Colors.white
),
)
),
BottomNavyBarItem(
textAlign: TextAlign.center,
activeColor: Color(0xFF010101),
title: Text(' CART',style: TextStyle(
color:Style.Colors.mainColor,fontSize: 13.0
)),
icon: Padding(
padding: EdgeInsets.only(left:5.0),
child: Badge(
badgeColor: Style.Colors.mainColor,
badgeContent: Text(cartBloc.cart.length.toString(),style: TextStyle(fontWeight: FontWeight.bold),), //not updating when select item
child: Icon(
SimpleLineIcons.basket,
size:18.0,
color:_currentIndex == 1 ? Style.Colors.mainColor:Colors.white
),
)
)
),
]
),
),
);
}
}
There are a lot of things you're not doing correctly using Bloc.
For example:
Your CartBloc needs to extend the Bloc class like so:
class CartBloc extends Bloc<SomeEvent, SomeState>
Then you need to override the mapEventToState method in that class like this.
#override
Stream<SomeState> mapEventToState(SomeEvent event) async* {
Then you need to provide your Bloc using:
BlocProvider(
create: (_) => CartBloc(),
child: YourWidget()
)
And then use BlocBuilder to build your widget using the data from the CartBloc
I suggest you take a look at the documentation and the example on how to use bloc
https://pub.dev/packages/flutter_bloc/example
Related
I have an app I am working on. I used sharedpreferences and provider to store the index of the pageview of the screen so that when I click on a button from the home page, it navigates to the PageViewScreen and will continue from the last index page of the pageView screen I was before leaving the app. However I want to make the app to start from the first index if the date changes to another date.
To explain further, If user opens the app 28th of January in the morning, I would love to store the last page index they were on before leaving the app. If user then come back in the afternoon, I would want that they continue from where they stop. However if the time changes to 29th of January from exactly 12am, I want to clear the data saved in sharedpreferences so that they can start from first index.
I understand that I will use sharedpreference clear or remove method but I do not understand how to track each day so I can use the method.
I have created the code to save the last index but remains how to clear the last index for every date change.
here is my provider and shared preference file
import 'package:flutter/cupertino.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ProviderData extends ChangeNotifier {
SharedPreferences? prefs;
final String pageIndexKey = 'pageIndex';
int? _pageIndex;
ProviderData() {
_pageIndex = 0;
loadFromPrefs();
}
_initPrefs() async {
prefs ??= await SharedPreferences.getInstance();
}
loadFromPrefs() async {
await _initPrefs();
_pageIndex = prefs!.getInt(pageIndexKey) ?? 0;
notifyListeners();
return _pageIndex;
}
_savePageIndexToPrefs({required String key, required int value}) async {
await _initPrefs();
prefs!.setInt(key, value);
}
void changePageIndex(int newPageIndex) {
_pageIndex = newPageIndex;
_savePageIndexToPrefs(key: pageIndexKey, value: _pageIndex!);
notifyListeners();
}
}
below is my PageViewScreen file,
I use Future builder to get my last index so I can build my app based on the last index
Future getProvider() {
return ProviderData().loadFromPrefs().then((value) {
return PageController(initialPage: value - 1);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: getProvider(),
builder: ((context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return Center(
child: Text(
'${snapshot.error} occurred',
style: TextStyle(fontSize: 18),
));
} else if (snapshot.hasData) {
final control = snapshot.data;
return Stack(
children: [
Consumer<ProviderData>(
builder: (context, providerData, _) {
return PageView(
controller: control,
onPageChanged: (index) {
///// I saved the index of the pageview here but I would love to clear it and make it start from index1 for every new date i.e 12am of everyday.
providerData.changePageIndex(index);
// setState(() {
// onLastPage = index == 2;
// onFirstPage = index == 0;
// onSecondPage = index == 1;
// onThirdPage = index == 2;
// onFourthPage = index == 3;
// onFifthPage = index == 4;
// onSixthPage = index == 5;
// onSeventhPage = index == 6;
// });
},
children: _pageList,
);
},
),
Align(
alignment: Alignment(0, -0.8),
child: Consumer<ProviderData>(
builder: (context, providerData, _) {
return SmoothPageIndicator(
controller: control,
count: 13,
effect: SlideEffect(
activeDotColor: Color.fromRGBO(215, 60, 16, 1)),
);
}),
),
Align(
alignment: Alignment(0, 0.5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
GestureDetector(
onTap: (() {
control.previousPage(
duration: Duration(milliseconds: 500),
curve: Curves.easeIn);
}),
child: Text(
"prev",
style: TextStyle(
fontWeight: FontWeight.w600, fontSize: 16),
),
),
GestureDetector(
onTap: (() {
control.nextPage(
curve: Curves.easeIn,
duration: Duration(milliseconds: 500));
}),
child: Text(
"Next",
style: TextStyle(
fontWeight: FontWeight.w600, fontSize: 16),
),
),
],
),
)
],
);
}
}
return Center(child: CircularProgressIndicator());
}
)
)
);
}
Please I need someone that can help me solve and approach this issue.
Thank you in advance
You can keep the date of last change inside the Shared Preferences, and to check it every time when you load the preferences. It's important to keep the date without time and minutes, so recreating the date during the same day will have the same value.
You can simplify the provider logic and only use it as Provider instead of ChangeNotifier.
Here is a working example:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final sharedPrefs = await SharedPreferences.getInstance();
runApp(
Provider(
create: (context) => ProviderData(sharedPrefs),
child: const MainApp(),
),
);
}
class MainApp extends StatefulWidget {
const MainApp({super.key});
#override
State<MainApp> createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
final _pages = [
const Center(
child: Text(
'Page 1',
style: TextStyle(fontSize: 38),
),
),
const Center(
child: Text(
'Page 2',
style: TextStyle(fontSize: 38),
),
),
const Center(
child: Text(
'Page 3',
style: TextStyle(fontSize: 38),
),
),
const Center(
child: Text(
'Page 4',
style: TextStyle(fontSize: 38),
),
),
];
late final PageController _pageController;
late final ProviderData _providerData;
bool loading = true;
#override
void initState() {
WidgetsBinding.instance.scheduleFrameCallback((timeStamp) {
_providerData = context.read<ProviderData>();
_pageController = PageController(initialPage: _providerData.getPageIndex());
setState(() => loading = false);
});
super.initState();
}
#override
void dispose() {
_pageController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: loading
? const Center(child: CircularProgressIndicator())
: Scaffold(
body: Stack(
children: [
PageView(
controller: _pageController,
children: _pages,
onPageChanged: (value) => _providerData.savePageIndexToPrefs(value: value),
),
Align(
alignment: const Alignment(0, -0.8),
child: SmoothPageIndicator(
controller: _pageController,
count: _pages.length,
effect: const SlideEffect(activeDotColor: Color.fromRGBO(215, 60, 16, 1)),
),
),
Align(
alignment: const Alignment(0, 0.5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
GestureDetector(
onTap: (() {
_pageController.previousPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn);
}),
child: const Text(
"Prev",
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
),
),
GestureDetector(
onTap: (() {
_pageController.nextPage(curve: Curves.easeIn, duration: const Duration(milliseconds: 500));
}),
child: const Text(
"Next",
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
),
),
],
),
)
],
),
),
);
}
}
class ProviderData {
ProviderData(this._prefs);
final SharedPreferences _prefs;
static const String _pageIndexKey = 'pageIndex';
static const String _pageIndexLastUpdated = 'pageIndexLastUpdatedKey';
int getPageIndex() {
final lastUpdatedStr = _prefs.getString(_pageIndexLastUpdated);
if (lastUpdatedStr != null) {
final lastUpdated = DateTime.parse(lastUpdatedStr);
final now = DateTime.now();
// creating a date without hours and minutes
final today = DateTime.utc(now.year, now.month, now.day);
if (lastUpdated.isAtSameMomentAs(today)) {
final pageIndex = _prefs.getInt(_pageIndexKey) ?? 0;
return pageIndex;
}
}
_prefs.remove(_pageIndexKey);
return 0;
}
void savePageIndexToPrefs({required int value}) {
_prefs.setInt(_pageIndexKey, value);
final now = DateTime.now();
// creating a date without hours and minutes
final today = DateTime.utc(now.year, now.month, now.day);
_prefs.setString(_pageIndexLastUpdated, today.toIso8601String());
}
}
I'm trying to create a pageview that I can load a widget into that is defined in another file. This works fine, except when I try to add a callback, I get the following error:
FlutterError (setState() or markNeedsBuild() called during build.
This error is triggered when the email entered is considered to be valid (that is, when the code in the email_entry.dart calls the callback function that was passed from the account_onboarding.dart file.) I haven't been able to determine why this is happening, and no tutorials on this subject seem to exist. I am still pretty new to Dart/Flutter, so I'm hoping someone can point out what's happening (and a fix) here.
Here is my code:
-Parent widget, account_onboarding.dart
import 'package:flutter/material.dart';
import 'package:page_view_indicators/page_view_indicators.dart';
import 'package:animated_title_screen/screens/email_entry.dart';
class AccountOnboarding extends StatefulWidget {
const AccountOnboarding({Key? key}) : super(key: key);
#override
State<AccountOnboarding> createState() => _AccountOnboardingState();
}
class _AccountOnboardingState extends State<AccountOnboarding> {
final _pController = PageController(initialPage: 0);
final _currentPageNotifier = ValueNotifier<int>(0);
final List<Widget> _pages = [];
bool validEmail = false; //Callback should set this variable
#override
void initState() {
super.initState();
_pages.add( //Add the EmailEntry widget to the list
EmailEntry(emailIsValid: (p0) {
setState(() {
validEmail = p0;
});
},),
);
_pages.add(
Container(
color: Colors.blue,
child: Text("Pg2"),
),
);
_pages.add(
Container(
color: Colors.green,
child: Text("Pg3"),
),
);
}
#override
void dispose() {
_pController.dispose();
_currentPageNotifier.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Create Account",
style: Theme.of(context).textTheme.headline5,
),
centerTitle: true,
actions: [
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
],
),
body: Stack(
fit: StackFit.expand,
children: [
Column(
children: [
Row(
children: [
Text(
"Step ${_currentPageNotifier.value + 1} of ${_pages.length}",
),
CirclePageIndicator(
dotColor: const Color(0xFF323232),
selectedDotColor: const Color(0xFFE4231F),
size: 10,
selectedSize: 10,
currentPageNotifier: _currentPageNotifier,
itemCount: _pages.length,
),
],
),
PageView(
controller: _pController,
onPageChanged: (index) {
setState(() {
_currentPageNotifier.value = index;
});
},
children: [
for (Widget p in _pages) p, //Display all pages in _pages[]
],
),
ElevatedButton(
child: const Text("Continue"),
onPressed: () => print("Pressed 2"),
style: ElevatedButton.styleFrom(
primary: validEmail ? const Color(0xFFE1251B) : Colors.black,
textStyle: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
],
),
],
),
);
}
}
Here is the email_entry.dart code:
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class EmailEntry extends StatefulWidget {
final Function(bool) emailIsValid; //Function required in constructor
const EmailEntry({Key? key, required this.emailIsValid}) : super(key: key);
#override
State<EmailEntry> createState() => _EmailEntryState();
}
class _EmailEntryState extends State<EmailEntry> {
final _emailController = TextEditingController();
FocusNode _emailFocus = FocusNode();
#override
void initState() {
super.initState();
_emailController.addListener(() => setState(() {}));
_emailFocus.addListener(() {
print("Focus email");
});
}
#override
void dispose() {
_emailController.dispose();
super.dispose();
}
bool validateEmail(String email) {
bool valid = RegExp(
r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+#[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)*$")
.hasMatch(email);
if (valid) {
widget.emailIsValid(true); //Call the callback function
}
return valid;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Column(
children: [
Text(
"YOUR EMAIL",
style: Theme.of(context).textTheme.headline2,
),
Text(
"Please use an email address that you would like to make your login.",
style: Theme.of(context).textTheme.bodyText2,
textAlign: TextAlign.center,
),
Container(
child: Text(
"Email Address",
),
),
TextField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
focusNode: _emailFocus,
suffixIcon: getTextFieldSuffix(emailController, _emailFocus), //OFFENDING CODE
),
],
),
],
),
);
}
//THIS FUNCTION CAUSED THE ISSUE. It is code I got from a youtube //tutorial. Probably should have guessed.
Widget getTextFieldSuffix(TextEditingController controller, FocusNode node) {
if (controller.text.isNotEmpty && node.hasFocus) {
return IconButton(
color: Colors.grey.withAlpha(150),
onPressed: () => controller.clear(),
icon: const Icon(Icons.close));
} else if (controller.text.isNotEmpty && !node.hasFocus) {
return const Icon(
Icons.check,
color: Colors.green,
);
}
return Container(
width: 0,
);
}
}
in initState,you need to call addPostFrameCallback.
like this...
#override
void initState() {
super.initState();
///Add This Line
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
///All of your code
});
}
I found out that there is some code in my production version that calls a redraw every time the user enters a letter into the textfield for the email address. This was causing a problem because the screen was already being redrawn, and I was calling setState to redraw it again. I will edit the code shown above to include the offending code.
i want to have my list be controlled by arrow buttons, one that moves it forward one item and one that moves it backward one item something like this
i tried many packages and solutions but most of them goes to the end of the list or a fixed index number what i want is for the list to move to the next item in the list
You can use scroll_to_index package providing AutoScrollTag.
class ST extends StatefulWidget {
const ST({Key? key}) : super(key: key);
#override
_STState createState() => _STState();
}
class _STState extends State<ST> {
final AutoScrollController controller = AutoScrollController();
late List<Widget> items;
int _currentFocusedIndex = 0;
#override
void initState() {
items = List.generate(
33,
(index) => AutoScrollTag(
key: ValueKey(index),
controller: controller,
index: index,
child: Container(
height: 100,
width: 100,
alignment: Alignment.center,
color: index.isEven ? Colors.deepOrange : Colors.deepPurple,
child: Text(index.toString()),
),
),
);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
IconButton(
onPressed: () {
_currentFocusedIndex--;
if (_currentFocusedIndex < 0) {
_currentFocusedIndex = items.length - 1;
}
controller.scrollToIndex(_currentFocusedIndex,
preferPosition: AutoScrollPosition.begin);
setState(() {});
},
icon: const Icon(Icons.arrow_back_ios_new_sharp),
),
Expanded(
child: ListView(
scrollDirection: Axis.horizontal,
controller: controller,
children: items,
),
),
IconButton(
onPressed: () {
_currentFocusedIndex++;
if (_currentFocusedIndex > items.length) {
_currentFocusedIndex = 0;
}
controller.scrollToIndex(_currentFocusedIndex,
preferPosition: AutoScrollPosition.begin);
setState(() {});
},
icon: const Icon(Icons.arrow_forward_ios_sharp),
),
],
),
);
}
}
It can be done only if you know the exact width to be scrolled. Use scrollController
ScrollController scrollController = ScrollController(
initialScrollOffset: 100, // or whatever offset you wish
keepScrollOffset: true,
);
I am getting the error quoted above when I try to rebuild a class. The class I am calling, ListOfIngs(), is a class that basically creates a textField, but my goal is to create a large amount of TextFields, the variable countIngs holds the value for the exact number, in a listView. Here is the code:
class NewGroceryList extends State<NewGroceryListWidget> {
final GlobalKey<_ListOfIngsState> _key = GlobalKey();
final myController = new TextEditingController();
int countings = 0;
Widget build(BuildContext context) {
debugPrint(myController.text);
return Scaffold(
appBar: AppBar(
title: Text("New Grocery List"),
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios, color: Colors.white),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ListsPage()),
);
},
),
actions: <Widget>[
IconButton(
icon: new Icon(Icons.check, color: Colors.white),
onPressed: () {})
],
),
drawer: AppDrawer(),
body: Container(
padding: EdgeInsets.fromLTRB(10.0, 20.0, 10.0, 30.0),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Text('Ingredients',
style: GoogleFonts.biryani(fontSize: 15.0)),
IconButton(
icon: new Icon(Icons.add),
onPressed: () {
setState(() {
countings++;
});
debugPrint('$countings');
},
)
],
),
setState(() {
ListOfIngsWidget(countings);
}),
],
),
));
}
}
Here is the ListOfIngs class/Widget:
class ListOfIngsWidget extends StatefulWidget {
final int countIngs;
const ListOfIngsWidget(this.countIngs, {Key key}) : super(key: key);
#override
_ListOfIngsState createState() => _ListOfIngsState();
}
class _ListOfIngsState extends State<ListOfIngsWidget> {
List<TextEditingController> _controllerList = [];
List<TextField> _textFieldList = [];
#override
void initState() {
for (int i = 1; i <= widget.countIngs; i++) {
TextEditingController controller = TextEditingController();
TextField textField = TextField(
controller: controller,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Ingredient $i',
),
);
_textFieldList.add(textField);
_controllerList.add(controller);
}
super.initState();
}
#override
Widget build(BuildContext context) {
return new Container(
child: Flexible(
child: ListView(children: _textFieldList),
),
);
}
}
Remove the setState around the ListOfIngsWidget and add a UniqueKey() to it like so:
ListOfIngsWidget(countings, key: UniqueKey()).
Here I have a trimmed down page which creates a ReorderableListView, which has its body set to two RecipeStepWidgets with UniqueKeys set (I've also tried this with ValueKey and ObjectKey)
import 'package:flutter/material.dart';
import 'consts.dart';
import 'recipeStep.dart';
import 'recipeStepWidget.dart';
class NewRecipePage extends StatefulWidget {
#override
_NewRecipePageState createState() => _NewRecipePageState();
}
class _NewRecipePageState extends State<NewRecipePage> {
final TextEditingController _nameController = TextEditingController();
#override
Widget build(BuildContext context) {
List<RecipeStep> recipeSteps = [];
List<RecipeStepWidget> stepWidgets = [
RecipeStepWidget(key: UniqueKey()),
RecipeStepWidget(key: UniqueKey())
];
void _onReorder(int oldIndex, int newIndex) {
setState(
() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
final RecipeStepWidget item = stepWidgets.removeAt(oldIndex);
stepWidgets.insert(newIndex, item);
},
);
}
return Scaffold(
appBar: AppBar(title: Text("New Recipe")),
body: Column(
children: <Widget>[
Expanded(
child: ReorderableListView(
header: Text("Steps"),
onReorder: _onReorder,
children: stepWidgets,
),
),
],
),
);
}
}
The RecipeStepWidget class is (ignoring includes):
class RecipeStepWidget extends StatefulWidget {
RecipeStepWidget({#required Key key}) : super(key: key);
_RecipeStepWidgetState createState() => _RecipeStepWidgetState();
}
class _RecipeStepWidgetState extends State<RecipeStepWidget> {
TextEditingController _controller = TextEditingController();
TextEditingController _durationLowController = TextEditingController();
TextEditingController _durationHighController = TextEditingController();
bool concurrent = false;
RecipeStep toRecipeStep() {
return RecipeStep(
text: _controller.text,
);
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(
controller: _controller,
decoration: InputDecoration(hintText: "Step text"),
),
Row(
children: <Widget>[
Text("Duration min: "),
Container(
width: 40.0,
//height: 100.0,
child: TextField(
controller: _durationLowController,
keyboardType: TextInputType.number,
decoration: InputDecoration(hintText: "0"),
onChanged: (String val) {
if (_durationHighController.text.isEmpty ||
int.parse(val) >
int.parse(_durationHighController.text)) {
_durationHighController.text = val;
}
},
),
),
Text(" max: "),
Container(
width: 40.0,
//height: 100.0,
child: TextField(
controller: _durationHighController,
keyboardType: TextInputType.number,
decoration: InputDecoration(hintText: "0"),
),
),
],
),
Row(
children: <Widget>[
Text("Start concurrently with previous step"),
Checkbox(
value: concurrent,
onChanged: (bool val) => {
setState(() {
concurrent = val;
})
}),
],
),
],
);
}
}
When I edit the textfields or checkboxes in the RecipeStateWidgets and then reorder them within the list by clicking+dragging them, the widgets get reset to their default state.
Does anyone have any ideas why this is happening? I thought that all I had to do in order to get the ReorderableListView to work as intended was to set a key on each of its children. Is that not correct?
Thanks!
I think you can use AutomaticKeepAliveClientMixin like so:
class _RecipeStepWidgetState extends State<RecipeStepWidget> with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
Giving the list item a ValueKey seems to fix the problem for me. Hopefully helps in your situation as well.
e.g.
List<YourModel> _items = [];
Widget _itemsListWidget() {
return ReorderableListView(
onReorder: (oldIndex, newIndex) {
//
},
children: [
for (int index = 0; index < _items.length; index += 1)
Text(
_items[index],
key: ValueKey(_items[index].id), // <--- This is what you need to add
),
],
);
}