I was reading that flutter bloc pattern is best for managing state.
I can see the need to separate display and business logic into separate areas.
Trying to learn flutter/dart from ground up.
I building a listview and each row of individual items that acts as a shopping card. User can select # of items or flavors of bagels in each row.
first issue is how to display the count of variable _dozen on screen? I have cubit/bloc sample code working. Trying to add simple test logic to when use select intervals of 13 bagels it increment a count of 1 bakers = _dozen. I can see this in debugger works . But I cannot ( sorry being new ) cannot figure out how to pass the _dozen variable to my view_page. Is passing in Bloc on for state management correct? or do I fall back and try inheritabve widget?
at end of day just want to display at bottom of screen total dozen bagels order.
import 'package:bloc/bloc.dart';
/// {#template counter_cubit}
/// A [Cubit] which manages an [int] as its state.
/// {#endtemplate}
class CounterCubit extends Cubit<int> {
/// {#macro counter_cubit}
CounterCubit() : super(12);
int _dozen = 12;
/// Add 1 to the current state.
void increment() {
if (state >= 0) {
emit(state + 1);
_dozen = ( state ~/ 13);
print('dozen:' + '$_dozen');
}
}
/// Subtract 1 from the current state.
void decrement() {
if (state > 0) {
emit(state - 1);
_dozen = ( state ~/ 13);
print('dozen:' + '$_dozen');
}
}
}
It seems that you need to keep more than one int in your state; one for count and one for dozen. Make a separate class CounterState to represent your state and hold those values so you end up with this code:
import 'package:bloc/bloc.dart';
class CounterState {
int count;
int dozen;
CounterState(this.count, this.dozen);
}
/// {#template counter_cubit}
/// A [Cubit] which manages an [CounterState] as its state.
/// {#endtemplate}
class CounterCubit extends Cubit<CounterState> {
/// {#macro counter_cubit}
CounterCubit() : super(CounterState(12,1));
/// Add 1 to the current state.
void increment() {
if (state.count >= 0) {
final int count = state.count + 1;
emit(CounterState(count, count ~/ 12));
print('dozen:' + '${state.dozen}');
}
}
/// Subtract 1 from the current state.
void decrement() {
if (state.count > 0) {
final int count = state.count - 1;
emit(CounterState(count, count ~/ 12));
print('dozen:' + '${state.dozen}');
}
}
}
Then in your Widgets, access this as state.count and state.dozen instead of just state.
This solution can be improved, but it is sufficient to get you going.
Related
I have a count variable like this:
class Count with ChangeNotifier {
int _count = 1;
int get count => _count;
void setCount(bool isIncrement) {
if (isIncrement) {
_count = checkCount(_count + 1);
} else {
_count = checkCount(_count - 1);
print("decrement" + _count.toString());
}
notifyListeners();
}
int checkCount(int count) {
if (count < 0) {
return 0;
} else if (count > 10) {
return 10;
} else {
return count;
}
}
}
I'm using the provider to use it on the food pages. But when I switch between the pages, the count variable continues from where it left off. I want it to restart as 1 every time the page changes.
class Hamburger extends StatefulWidget {
const Hamburger({super.key, required Count count});
#override
State<Hamburger> createState() => _HamburgerState();
}
class _HamburgerState extends State<Hamburger> {
#override
Widget build(BuildContext context) {
Color coloricon = Provider.of<iconcolor>(context).coloricon;
int count = Provider.of<Count>(context).count;
return Scaffold(
…
);
}
}
The idea of Provider is to have a consistent state across all your pages.
If you want to save only the number of hamburgers selected you need a variable similar to _hamburgerCount and control it there.
Or, for a more scalable solution, you can create a Map<String, int> or something similar to store the amount of each type. There is many ways to solve this, but with one global count variable you can not achieve storing a count for each type of food.
reading the Getx package documentation, I faced this method:
Get.create<ShoppingController>(() => ShoppingController());
and it says:
Get.create(()=>Controller()) will generate a new Controller each time you call Get.find(),
but, I don't seem to understand what this means and how it differs from the Get.put() and Get.lazyPut().
I found the answer for that question.
The big difference between
Get.create<ShoppingController>(() => ShoppingController());
And :
Get.put(ShoppingController());
Both of them are used in injecting dependencies in the a Flutter app, But Get.put<T>(T()) injects it just one time and whenever we call Get.find<T>() it looks up for that exact dependency and return it, so we can rememeber this:
Get.put<T>(()) inject a dependency and whatever time we call Get.find<T>() across the entire app, the same T is returned.
In the other side, Get.create<V>(() => V) also inject a dependency in a Flutter app, But every time we call Get.find<V>(), it doesn't return the same V, it creates a new instance of V, then return it, so we can rememeber this:
Get.create<V>(() => V) don't return the same instance, it creates a new one every time Get.find<V>() is called.
Get.put(T()) :
class ControllerOne extends GetxController {
int number = 10;
increment() {
number += 10;
}
}
final controllerOne = Get.put<ControllerOne>(ControllerOne());
final controllerOneFinder = Get.find<controllerOne>();
controllerOneFinder.increment();
final controllerOneSecondFinder = Get.find<controllerOne>();
print(controllerOneFinder.number); // 20
print(controllerOneSecondFinder.number); // 20
it stay the same.
Get.create(() =>T) :
class ControllerTwo extends GetxController {
int secondNumber = 10;
increment() {
secondNumber += 10;
}
}
final controllerTwo = Get.create<ControllerTwo>(() => (ControllerTwo());
final controllerTwoFinder = Get.find<ControllerTwo>();
controllerTwoFinder.increment();
final controllerTwoSecondFinder = Get.find<ControllerTwo>();
print(controllerTwoFinder.number); // 20
print(controllerTwoSecondFinder.number); // 10
Each one is different than the other. (controllerTwoSecondFinder == controllerTwoFinder) is false.
im new to Riverpod and Flutter
the problem i'm having is that when i use StateNotifier, the state is immutable so i have to create new state and add new value to the end of List
void add(Cart cart) {
state = [...state, cart];
}
void inQuan(int i) {
final tempCart = state[i];
tempCart.quantity++;
remove(state[i]);
add(tempCart);
}
and this makes my list pushes the item i'm editing to the end
is there any way to optimize my code?
This creates only one new array.
void inQuan(int i) {
state = [
for (int index = 0; i < state.length; index++)
if (i == index)
state[i].copyWith(quantity: state[i].quantity++)
else
state[i]
];
}
I am currently learning River Pod and also new to flutter.
When setting new state in StateNotifer , I need to create a new model and replace the state
But directly changing is not working
class CounterModel {
CounterModel(this.count, this.age);
int count;
int age;
}
class CounterNotifier extends StateNotifier<CounterModel> {
CounterNotifier() : super(_initialValue);
static CounterModel _initialValue = CounterModel(0,18);
void increment() {
// state.count = state.count + 1; // not working, but need like this !
state = CounterModel(state.count + 1, state.age); // working
}
}
In the above code , when I trying to change the count variable directly like, state.count = state.count + 1 , nothing changed
But when reinitialising the state by creating a new model like state = CounterModel(state.count + 1, state.age)
Its seems to be state model variables are immutable and needs to be recreated on every alteration !
My question is , what if the CounterModel have 50 variables , then I have to do something like
state = CounterModel (var1,var2,......,var49,var50) ;
So , is it possible to directly change the variables like
state.var1 = new_value1;
state.var2 = new_value2;
....
state.var50 = new_value50;
You have to always reassign the state in StateNotifier for Consumers to see the changes hence, state.var1 = new_value1; can't work with StateNotifier
If You're very keen about that syntax, use ChangeNotifier since it allows you change individual properties of the class but you must call notifyListeners
Like so:
class CounterNotifier extends StateNotifier {
static CounterModel value = CounterModel(0,18);
void increment() {
value.count = value.count + 1;
notifyListeners();
}
}
If You want to stick with StateNotifier and don't want to write boilerplate code, create a copyWith method on the model.
Like so:
class CounterModel {
CounterModel(this.count, this.age);
int count;
int age;
CounterModel copyWith({int? count, int? age}){
return CounterModel(
count ?? this.count,
age ?? this.age
);
}
}
Then you can keep reassigning with it like so:
class CounterNotifier extends StateNotifier<CounterModel> {
CounterNotifier() : super(_initialValue);
static CounterModel _initialValue = CounterModel(0,18);
void increment() {
state = state.copyWith(count: state.count + 1);
}
}
As you can see from my code sample, I'm using this variable. I also reference multiple times later in the class.
Flutter Warning - info: The value of the field '_loadTimer' isn't used. (unused_field at [app] lib/models/knowledge_level/pb_cycle_permissions_collection.dart:12)
ng is: info: The value of the field '_loadTimer' isn't used. (unused_field at [app] lib/models/knowledge_level/pb_cycle_permissions_collection.dart:12)
import 'dart:async';
import 'dart:collection';
import 'package:app/data/graphql/queries.dart';
import 'package:app/helpers/shared_logger.dart';
import 'package:flutter/cupertino.dart';
import '../command_permission.dart';
class PBCyclePermissionsCollection
with ListMixin<CommandPermission>, ChangeNotifier {
Timer? _loadTimer;
///
/// CONSTRUCTION AND INITIALIZATION
///
static final PBCyclePermissionsCollection _instance =
PBCyclePermissionsCollection._internal();
factory PBCyclePermissionsCollection() {
return _instance;
}
/// ACCESS SINGLETON VIA myPBCyclePermInstance = PBCyclePermissionsCollection()
PBCyclePermissionsCollection._internal() {
_loadTimer = Timer(_waitFirstLoad, _attemptLoad);
}
///
/// PRIVATE VARIABLES AND METHODS
///
static final Duration _waitFirstLoad = Duration(milliseconds: 500);
static final Duration _waitRetryLoad = Duration(seconds: 2);
static final int _maxAttempts = 4;
int _loadAttempts = 0;
bool _isReady = false;
bool _hasFailed = false;
/// Storage of CommandPermissions List once loaded
final List<CommandPermission> _list = [];
void _attemptLoad() async {
_loadAttempts++;
SharedLogger.I().d('_attemptLoad() current load attempt: ${_loadAttempts}');
try {
final results = await Queries.getCommandPermissions();
var data = results.data!['commandPermissions'];
var permissions = <CommandPermission>[];
for (var item in data) {
permissions.add(CommandPermission.fromJson(item));
}
/// Populated class with loaded objects.
_list.clear();
_list.addAll(permissions);
_isReady = true;
notifyListeners();
} catch (e) {
SharedLogger.I().e('Error loading PBCycle Permissions - ${e}');
_newAttempt();
}
}
void _newAttempt() {
SharedLogger.I().d(
'_newTry() _loadAttempts: ${_loadAttempts} _maxAttempts:${_maxAttempts} '
'creating new loadTimer for another try? : ${!(_loadAttempts >= _maxAttempts)}');
if (_loadAttempts >= _maxAttempts) {
_hasFailed = true;
notifyListeners();
// TODO: do we invalidate any existing data that may have been loaded before? Like if this load cycle is a refresh?
// If so, we should reset _isReady and _list;
return;
}
_loadTimer = Timer(_waitRetryLoad, _attemptLoad);
}
///
/// PUBLIC METHODS
///
bool get isLoaded {
return _isReady;
}
bool get hasFailed {
return _hasFailed;
}
#override
set length(int newLength) {
throw ('length cannot be changed externally');
}
#override
int get length {
return _list.length;
}
#override
CommandPermission operator [](int index) {
return _list[index];
}
#override
void operator []=(int index, CommandPermission value) {
throw ('Cannot modify list from outside');
}
}
Image of IDE with Code Sample and associated Dart Analysis Hints
You aren't actually using it, you're just setting the value multiple times
The answer from Andrew is correct, but a bit unclear since unsure what 'it' refers to. Here's another way to explain what the warning message means:
Notice that the message says you are not using the value. You are using the variable, but not its value. You are assigning the value. To read the value would be using it.
That said, the question is answered, but I think the question is somewhat vague by asking "what am i missing". What do you (OP) want to achieve? I assume it's to not see that warning anymore. And that is what brings me to this post. I have similar issue. I too have a class variable for a Timer and I get this same warning message. One does not need to read the value in order to use a timer but the analyzer doesn't know that. While writing this response I have discovered that you can a suppress warning. How about this:
// ignore: unused_field
Timer? _loadTimer;