I want to make a coffee machine app
I want to update a checkbox in flutter bloc package,
I use bloc to state management
I try searching on the web but I don't have a good answer to this problem
tips to help me, my files are named AppCubit is a bloc file and AppState is states and ChangeCheckBox and the screen MainModule
AppCubit is cubit class
class AppCubit extends Cubit<AppState> {
AppCubit() : super(AppInitial());
// value in
bool isWithCreamChecked = false;
static AppCubit get(BuildContext context) => BlocProvider.of(context);
// this is a function for changing the value of the checkbox
void checkBox({bool? value, bool? changeValue}) {
changeValue = value!;
emit(ChangeCheckBox(value,changeValue));
}
}
AppState all states in the app
abstract class AppState {}
class AppInitial extends AppState {}
// checkbox states
class ChangeCheckBox extends AppState {
bool value;
bool changeValue;
ChangeCheckBox(this.value,this.changeValue);
}
in MainModule
Checkbox(
value: AppCubit.get(context).isWithCreamChecked,
onChanged: (bool? value) {
AppCubit.get(context).isWithCreamChecked = value!;
},
),
const Text('adding Cream to coffee, price is 20')
Checkbox(
value: AppCubit.get(context).isWithCreamChecked,
onChanged: (bool? value) {
AppCubit.get(context).checkBox(
value: value!,
changeValue:AppCubit.get(context).isWithCreamChecked
);
},
),
const Text('adding cream to coffee, price is 10')
I think this is because you are putting ! with value two times (value!). One before sending it to checkbox function here.
AppCubit.get(context).checkBox(
value: value!,
changeValue:AppCubit.get(context).isWithCreamChecked
);
Then again when you are setting the checkbox inside the cubit.
void checkBox({bool? value, bool? changeValue}) {
changeValue = value!;
emit(ChangeCheckBox(value,changeValue));
}
This will leads to making it the same value as you got initially.
Just do it once to change values. Also put ! before the value for changing the value before setting it like changeValue = !value
Related
I want to change date field value while clicking on a Radio Button named "Yes". One can input date by changing date from datepicker. Another one is is user clicked on "Yes" button the datefield value will be changed. I'm trying it using Provider. But the updated value isn't displaying into datefield instantly.
Code snippet:
DateTimeFormField(
dateFormat: DateFormat('yyyy-MM-dd'),
mode: DateTimeFieldPickerMode.date,
initialValue: DateTime.parse(list[index].endDate!),
decoration: InputDecoration(
isDense: true,
contentPadding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h),
hintStyle: TextStyle(color: Colors.black45),
errorStyle: TextStyle(color: Colors.redAccent),
border: OutlineInputBorder(),
suffixIcon: Icon(Icons.event_note),
),
onDateSelected: (DateTime value) {
list[index].endDate = value.toString();
},
)
##
class ManipulateDate extends ChangeNotifier {
String date = '';
void setDateToDTNow(String newDate) {
date = newDate;
notifyListeners();
}
}
Inside Button's onPressed function
Provider.of<ManipulateDate>(context, listen: false).setDateToDTNow(DateTime.now().toString());
How could I set the changed value Provider.of<ManipulateDate>(context).date into list[index].endDate and the value will display instantly while clicking on Button.
I guess I got the issue you have set listen: false -
Provider.of<ManipulateDate>(context, listen: false).setDateToDTNow(DateTime.now().toString());
So when you do notifylisteners() the above won't be triggered or updated so try changing that to true -
Provider.of<ManipulateDate>(context, listen: true).setDateToDTNow(DateTime.now().toString());
Or alternatively you can use a consumer widget around the part which you want to update in the UI. Hope these helps now....
Most likely this is because you're not listening to the provider changes in your build method, so the widget doesn't get "refreshed" when the provider is updated. Check ChangeNotifierProvider for some examples.
BTW, I highly recommend using Riverpod over Provider, as it offers a cleaner widget tree, and more flexibility in state management.
For example, you could do this:
// Outside of your class...
// Yes, as a global, but don't worry. That's actually the correct way to do it.
final selectedDateProvider = StateProvider((ref) => DateTime.now());
#override
Widget build(BuildContext context, WidgetRef ref) {
final DateTime selectedDate = ref.watch(selectedDateProvider).state;
return DateTimeFormField(
initialValue: selectedDate,
...
onDateSelected: (DateTime value) {
ref.read(selectedDateProvider).state = value;
},
);
}
On my screen, it has a list and i can choose some elements. When i choose i want to save it to a variable and can use it later in another screen. So i using cubit in here.I using like that :
My UI:
BlocProvider(
create: (context) => TherapyCubit(),
child: Wrap(
children: [
for (final therapy in therapyList)
EllipsisCard(
therapy: therapy,
)
],
),
)
EllipsisCard:
InkWell(
onTap: () {
setState(() {
isSelected = !isSelected;
});
if (isSelected) {
context.read<TherapyCubit>().addTherapy(widget.therapy);
} else {
context.read<TherapyCubit>().deleteTherapy(widget.therapy);
}
},
child: Container(..
Cubit:
class TherapyCubit extends Cubit<TherapyState> {
TherapyCubit() : super(TherapyInitial());
List<Therapy> selectedTherapies = [];
void addTherapy(Therapy therapy) {
selectedTherapies.add(therapy);
inspect(selectedTherapies);
}
void deleteTherapy(Therapy therapy) {
selectedTherapies.remove(therapy);
}
}
Cubit State:
abstract class TherapyState extends Equatable {
const TherapyState();
#override
List<Object> get props => [];
}
class TherapyInitial extends TherapyState {}
On my page i have a state named like "step". That thing has 2 step. When my step is 1 i showing first page and when my step is 2 i showing second page with Visibility.
But when i press back button i setting step state to 1 again so showing first page again but now i cant see any of my choosen elements. And on cubit my list (selectedTherapies) is being empty again. Why its being like that ? How can i solve it ?
There are two options:
Either the cubit is replaced (try adding a print inside your Cubit constructor to diagnose)
Or your UI isn't showing the selectedTherapies List properly
Where did you placed your BlocProvider inside the widget tree?
// this is my api resposne.
{
"statusCode": 200,
"status": "success",
"message": "translate data",
"result": [
{
"hello": "hello"
}
]
}
It's not recommended to use an API to localize your app's offline content because sometimes the user has to wait too much for your API to respond (also there's a possibility that it'd never respond).
The best way to do it is to make a dictionary because you already have offline texts and it's easy to translate them. then you can change texts completely offline.
Edit:
If you have no other option, here's the code that may help you:
import 'package:flutter/material.dart';
import 'dart:ui' as ui show TextHeightBehavior;
class LocalizedText extends StatefulWidget {
final String? data;
final TextStyle? style;
final StrutStyle? strutStyle;
final TextAlign? textAlign;
final TextDirection? textDirection;
final Locale? locale;
final bool? softWrap;
final TextOverflow? overflow;
final double? textScaleFactor;
final int? maxLines;
final String? semanticsLabel;
final TextWidthBasis? textWidthBasis;
final ui.TextHeightBehavior? textHeightBehavior;
const LocalizedText(this.data,
{Key? key,
this.style,
this.strutStyle,
this.textAlign,
this.textDirection,
this.locale,
this.softWrap,
this.overflow,
this.textScaleFactor,
this.maxLines,
this.semanticsLabel,
this.textWidthBasis,
this.textHeightBehavior})
: assert(
data != null,
'A non-null String must be provided to a Text widget.',
),
super(key: key);
#override
_LocalizedTextState createState() => _LocalizedTextState();
}
class _LocalizedTextState extends State<LocalizedText> {
bool _isLoading = true;
String? _translatedText;
#override
void initState() {
super.initState();
callApi();
}
#override
Widget build(BuildContext context) {
return Text(
_isLoading
? "Loding..." //Your Loading Text here, You can also use 'widget.data!' to show original text while waiting for Api result
: _translatedText ??
widget
.data!, //if the Api doesn't respond or fail, the original text will apear
key: widget.key,
locale: widget.locale,
maxLines: widget.maxLines,
overflow: widget.overflow,
semanticsLabel: widget.semanticsLabel,
softWrap: widget.softWrap,
strutStyle: widget.strutStyle,
style: widget.style,
textAlign: widget.textAlign,
textDirection: widget.textDirection,
textHeightBehavior: widget.textHeightBehavior,
textScaleFactor: widget.textScaleFactor,
textWidthBasis: widget.textWidthBasis,
);
}
void callApi() async {
var response;
//Call your API here
_isLoading = false;
if (response["statusCode"] == 200) {
_translatedText = response["result"]...; //You should set your translated text from Api to _translatedText
}
setState(() {});
}
}
Use it like this: LocalizedText("Hello")
If You want to optimize it more you can save the translated words locally and check them before calling the Api.
I have a setting page displaying a list view and each list tile displays the name of a user and a percentage which can be edited. By tapping on a list tile the user can change the percentage using a dialog.
I would like to update the state of a single list tile when the user saves the new value instead of building the entire page again (should be better for perfomance too). Is this possible?
I have already tried to export the list tile as a stateful widget (it must be stateful) and using it in my setting page but the setState() method does not work on the single tile at all. I don't know if it would somehow work with a notifier.
Here is how the page looks like (showing the dialog to set the new percentage):
enter image description here
Thanks in advance!
Edit: I added some code.
setting_page.dart
// this widget is called in a ListView.builder to bild the list tile.
// before creating the UserListTile widget I used to return the ListTile in
// _buildRow and call setState() but then everything is re-built again, which is what I don't want to
Widget _buildRow(User user) {
return UserListTile(
user: user, userDAO: _userDAO, myPercentage: _myPercentage);
}
user_list_tile.dart
import 'package:flutter/material.dart';
import 'package:myapp/classes/user.dart';
import 'package:myapp/database/user_dao.dart';
import 'package:numberpicker/numberpicker.dart';
class UserListTile extends StatefulWidget {
const UserListTile(
{Key? key,
required this.user,
required this.userDAO,
required this.myPercentage})
: super(key: key);
final User user;
final UserDAO userDAO;
final ValueNotifier<double> myPercentage;
#override
_UserListTileState createState() => _UserListTileState();
}
class _UserListTileState extends State<UserListTile> {
final TextStyle _biggerFont = const TextStyle(fontSize: 18);
double _currentDoubleValue = 10.0;
double? trailing;
#override
Widget build(BuildContext context) {
trailing = widget.user.percentage;
return ListTile(
title: Text(
widget.user.name,
style: _biggerFont,
),
trailing: Text('${trailing} %'),
onTap: () => _showDoubleDialog(widget.user).then((value) {
if (value != false && value != null) {
// some code to set the new percentage
User _updatedUser = User(
name: widget.user.name,
id: widget.user.id,
percentage: _newPercentage);
widget.userDAO.updateData(_updatedUser);
setState(() {}); // <- this setState does not update the single tile
}
}),
);
}
Future _showDoubleDialog(User user) {
_currentDoubleValue = user.percentage;
return showDialog(
// code to shod the dialog to edit the percentage
);
}
}
I am trying to make that when I submit the button, the isLoading variable is set to true, and when the block issues the state it returns to false, to show a circular progress indicator.
My problem is that I don't know WHERE or HOW to put that variable, nor how to tell the bloc that this variable is true before / outside the BlocBuilder,
this is the code of my button:
TextButton(
child: Text("submit"),
onPressed: () {
context
.read<ShowMusicHomeBloc>()
.add(OnSearchTracks(q: _searchText.text));
_searchText.clear();
},
),
Could you give me a practical example how I can set this variable isLoading to true using flutter_bloc?
You can make a LoadingState and emit it when you press the button.
abstract class MyState {}
class MyLoadingState extends MyState {}
class MyLoadedState extends MyState {
final MyData data;
MyLoadedState(this.data);
}
// other states like ErrorState or InitialState
class MyCubit extends Cubit<MyState>{
MyCubit(this.someService) : super(MyLoadingState());
final SomeService someService;
Future<void> fetchData(){
emit(MyLoadingState);
final fetchedData = someService.fetchData();
emit(MyLoadedState(fetchedData));
}
}
you can apply the same concept to Blocs too.