save state of filters in flutter - flutter

I have a code that is responsible for the data filter: the user make the criteria that are important to him and clicks "Apply". And sees a list based on the selected filters. But then these filters are reset, and if the user wants to make filters based on the previous ones, he has to select everything again. How to make that the state of the filters is saved and the user can continue filtering based on the previous request?
dialog_filter.dart
class FilterDialog extends StatefulWidget {
final void Function(Map<String, List<String>?>) onApplyFilters;
const FilterDialog({Key? key, required this.onApplyFilters}) : super(key: key);
#override
State<FilterDialog> createState() => _FilterDialogState();
}
class _FilterDialogState extends State<FilterDialog> {
Map<String, List<String>?> filters = {};
void _handleCheckFilter(bool checked, String key, String value) {
final currentFilters = filters[key] ?? [];
if(checked) {
currentFilters.add(value);
} else {
currentFilters.remove(value);
}
filters[key] = currentFilters;
}
page_main.dart
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
#override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
List<Phone> filteredPhones = phoneList;
void _filter(Map<String, List<String>?> filters) {
setState(() {
filteredPhones = phoneList;
filters.forEach((key, value) {
if((value ?? []).isNotEmpty) {
filteredPhones = filteredPhones.where((phone) {
switch(key) {}
}).toList();
}
});
});
}
class Filter {
String name;
bool Function(Phone) filterFn;
Filter({required this.name, required this.filterFn});
}

you need to use state management libraries like provider, bloc, mobx , riverpod etc.
So if you still want use stateful widget then you have to maintain a list of selected filters at parent level pass to your dialog and return back when user selects anything. This will be very complex and bug prone to do with only stateful widget.
If don't want use any libraries then you can use ValueNotifier or InheritedWidget. But I would suggest state management library is more easier. Start with provider it's easiest.

Related

Create / Manage dynamic TextEditingControllers

I got a ListView.builder that generates n number of elements and I am looking at adding a controller for each of them. I have seen some approaches of adding a controller to a list of controllers and then access them by the index however I am just wondering how will this impact the performance of the screen if lets say you have 20 controllers? Are there some best practices for this scenario? Should you even go down this line or avoid it?
I suggest to introduce a Widget for all items in list.
Make sure you dispose in a correct place for the performance.
Also I request to store the user entered value with the object of item will help to restore on scrolls.
Eg:
class YourWidget extends StatefulWidget {
const YourWidget({Key? key, required this.item}) : super(key: key);
final YourItem item;
#override
State<YourWidget> createState() => _YourWidgetState();
}
class _YourWidgetState extends State<YourWidget> {
final controller = TextEditingController();
#override
void initState() {
super.initState();
controller.text = widget.item.enteredValue;
}
#override
Widget build(BuildContext context) {
return TextField(
controller: controller,
onChanged: (value){
widget.item.enteredValue = value;
},
...
);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
}
class YourItem {
String? id;
...
String enteredValue = '';
}

Store the filters selected in a variable

I have a code that is responsible for filtering data: the user choose the criteria that are important to him and clicks "Apply". And sees a list based on the selected filters.
But the applied filters are not saved for subsequent filtrations. And the next time user click on the "filters" button, the user cannot continue working with them from the last moment. He has to choose all the filters again.
How to make the filters to be saved and the user to continue working with the filters based on the previous selection?
filter_dialog.dart
class FilterDialog extends StatefulWidget {
final void Function(Map<String, List<String>?>) onApplyFilters;
const FilterDialog({Key? key, required this.onApplyFilters}) : super(key: key);
#override
State<FilterDialog> createState() => _FilterDialogState();
}
class _FilterDialogState extends State<FilterDialog> {
Map<String, List<String>?> filters = {};
void _handleCheckFilter(bool checked, String key, String value) {
final currentFilters = filters[key] ?? [];
if(checked) {
currentFilters.add(value);
} else {
currentFilters.remove(value);
}
filters[key] = currentFilters;
}
#override
.......
main_page.dart
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
#override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
List<Phone> filteredPhones = phoneList;
void _filter(Map<String, List<String>?> filters) {
setState(() {
filteredPhones = phoneList;
filters.forEach((key, value) {
if((value ?? []).isNotEmpty) {
filteredPhones = filteredPhones.where((phone) {
switch(key) {
case 'brand':
return value!.contains(phone.brand);
case 'version_OS':
return value!.contains(phone.version_OS);
case 'operation_system':
return value!.contains(phone.operation_system);
default:
return false;
}
}).toList();
}
});
});
}
#override
....
}
class Filter {
String name;
bool Function(Phone) filterFn;
Filter({required this.name, required this.filterFn});
}
custom_checkbox_tile.dart
class CustomCheckboxTile extends StatefulWidget {
final String label;
final void Function(bool)? onChange;
const CustomCheckboxTile({Key? key, required this.label, this.onChange}) : super(key: key);
#override
State<CustomCheckboxTile> createState() => _CustomCheckboxTileState();
}
class _CustomCheckboxTileState extends State<CustomCheckboxTile> {
bool checked = false;
#override
Widget build(BuildContext context) {
return Row(
children: [
Checkbox(
visualDensity: VisualDensity.compact,
value: checked,
onChanged: (_) {
setState(() {
checked = !checked;
if(widget.onChange != null) {
widget.onChange!(checked);
}
});
},
),
Text(widget.label),
],
);
}
}
its not simple to answer your qustion.
More changes should made.
I'm done it all.
Please Check Github

Class to Class Access Data

I have a problem with classes. I keep the user data in user_info_screen with Google auth. How can I pull this user data in a class that has this data on another page?
class UserInfoScreen extends StatefulWidget {
UserInfoScreen({Key key, User user})
: _user = user,
super(key: key);
final User _user;
#override
_UserInfoScreenState createState() => _UserInfoScreenState();
}
class _UserInfoScreenState extends State<UserInfoScreen> {
User _user;
The page I want to use (user) by pulling this data
class HomeView extends StatefulWidget {
#override
_HomeViewState createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
}
Thank you.
A good way to handle it, espacially if you will use the user data on other pages, is to implement a store concept : https://pub.dev/packages/get
UserInfoScreen.dart
GetStorage box = GetStorage();
box.write('userData', _user);
HomeView.dart
GetStorage box = GetStorage();
User _user = box.read('userData');
Or pass it as an argument
UserInfoScreen.dart
Get.to(() => HomeView(), arguments: [
{"userData": _user}
]);
HomeView.dart
dynamic argumentData = Get.arguments;
#override
void onInit() {
print(argumentData[0]['userData']);
super.onInit();
}

How to access the root app state in flutter?

I have I loading indicator dialog in my main app component. I like to call showLoadingDlg from a sub component. I tried _AppLayoutPageState state = _AppLayoutPageState .of(context); but I'm able to import _AppLayoutPageState in my sub component.
class AppLayoutPage extends StatefulWidget {
AppLayoutPage({Key key, this.title}) : super(key: key);
final String title;
#override
_AppLayoutPageState createState() => _AppLayoutPageState();
}
class _AppLayoutPageState extends State<AppLayoutPage> {
bool _loading = false;
showLoadingDlg() {
setState(() {
_loading = true;
});
}
hideLoadingDlg() {
setState(() {
_loading = false;
});
}
...
I would advise you to use the bloc pattern and share a stream between your components, You can listen for the stream in your root widget and then you can send some data through the stream from your child widget to show the dialog.

Passing data to StatefulWidget and accessing it in it's state in Flutter

I have 2 screens in my Flutter app: a list of records and a screen for creating and editing records.
If I pass an object to the second screen that means I am going to edit this and if I pass null it means that I am creating a new item. The editing screen is a Stateful widget and I am not sure how to use this approach https://flutter.io/cookbook/navigation/passing-data/ for my case.
class RecordPage extends StatefulWidget {
final Record recordObject;
RecordPage({Key key, #required this.recordObject}) : super(key: key);
#override
_RecordPageState createState() => new _RecordPageState();
}
class _RecordPageState extends State<RecordPage> {
#override
Widget build(BuildContext context) {
//.....
}
}
How can I access recordObject inside _RecordPageState?
To use recordObject in _RecordPageState, you have to just write widget.objectname like below
class _RecordPageState extends State<RecordPage> {
#override
Widget build(BuildContext context) {
.....
widget.recordObject
.....
}
}
Full Example
You don't need to pass parameters to State using it's constructor.
You can easily access these using widget.myField.
class MyRecord extends StatefulWidget {
final String recordName;
const MyRecord(this.recordName);
#override
MyRecordState createState() => MyRecordState();
}
class MyRecordState extends State<MyRecord> {
#override
Widget build(BuildContext context) {
return Text(widget.recordName); // Here you direct access using widget
}
}
Pass your data when you Navigate screen :
Navigator.of(context).push(MaterialPageRoute(builder: (context) => MyRecord("WonderWorld")));
class RecordPage extends StatefulWidget {
final Record recordObject;
RecordPage({Key key, #required this.recordObject}) : super(key: key);
#override
_RecordPageState createState() => new _RecordPageState(recordObject);
}
class _RecordPageState extends State<RecordPage> {
Record recordObject
_RecordPageState(this. recordObject); //constructor
#override
Widget build(BuildContext context) {. //closure has access
//.....
}
}
example as below:
class nhaphangle extends StatefulWidget {
final String username;
final List<String> dshangle;// = ["1","2"];
const nhaphangle({ Key key, #required this.username,#required this.dshangle }) : super(key: key);
#override
_nhaphangleState createState() => _nhaphangleState();
}
class _nhaphangleState extends State<nhaphangle> {
TextEditingController mspController = TextEditingController();
TextEditingController soluongController = TextEditingController();
final scrollDirection = Axis.vertical;
DateTime Ngaysx = DateTime.now();
ScrollController _scrollController = new ScrollController();
ApiService _apiService;
List<String> titles = [];
#override
void initState() {
super.initState();
_apiService = ApiService();
titles = widget.dshangle; //here var is call and set to
}
I have to Navigate back to any one of the screens in the list pages but when I did that my onTap function stops working and navigation stops.
class MyBar extends StatefulWidget {
MyBar({this.pageNumber});
final pageNumber;
static const String id = 'mybar_screen';
#override
_MyBarState createState() => _MyBarState();
}
class _MyBarState extends State<MyBar> {
final List pages = [
NotificationScreen(),
AppointmentScreen(),
RequestBloodScreen(),
ProfileScreen(),
];
#override
Widget build(BuildContext context) {
var _selectedItemIndex = widget.pageNumber;
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
elevation: 0,
backgroundColor: Colors.white,
unselectedItemColor: Colors.grey.shade700,
selectedItemColor: Color(kAppColor),
selectedIconTheme: IconThemeData(color: Color(kAppColor)),
currentIndex: _selectedItemIndex,
type: BottomNavigationBarType.fixed,
onTap: (int index) {
setState(() {
_selectedItemIndex = index;
});
},
You should use a Pub/Sub mechanism.
I prefer to use Rx in many situations and languages. For Dart/Flutter this is the package: https://pub.dev/packages/rxdart
For example, you can use a BehaviorSubject to emit data from widget A, pass the stream to widget B which listens for changes and applies them inside the setState.
Widget A:
// initialize subject and put it into the Widget B
BehaviorSubject<LiveOutput> subject = BehaviorSubject();
late WidgetB widgetB = WidgetB(deviceOutput: subject);
// when you have to emit new data
subject.add(deviceOutput);
Widget B:
// add stream at class level
class WidgetB extends StatefulWidget {
final ValueStream<LiveOutput> deviceOutput;
const WidgetB({Key? key, required this.deviceOutput}) : super(key: key);
#override
State<WidgetB> createState() => _WidgetBState();
}
// listen for changes
#override
void initState() {
super.initState();
widget.deviceOutput.listen((event) {
print("new live output");
setState(() {
// do whatever you want
});
});
}
In my app, often instead of using stateful widgets, I use mainly ChangeNotifierProvider<T> in main.dart, some model class
class FooModel extends ChangeNotifier {
var _foo = false;
void changeFooState() {
_foo = true;
notifyListeners();
}
bool getFoo () => _foo;
}
and
var foo = context.read<FooModel>();
# or
var foo = context.watch<FooModel>();
in my stateless widgets. IMO this gives me more precise control over the rebuilding upon runtime state change, compared to stateful widgets.
The recipe can be found in the official docs, the concept is called "lifting state up".