How to display the saved state of a button when the page opens? - flutter

I have three buttons. The maximum that I can select (activate) is only one button. When switching buttons, I have activated should be true, and not activated - false. I write these values ​​to SharedPreferences for each button, store true or false. When I open the pages all the buttons are gray out (they are not selected). I need to save the button state that it was selected and display it when the page is opened. For example, I just need if the variable isVoltageAC = true, then the AC button will immediately turn purple when the page is opened. How to do it?
enum VoltageMode {
ac,
dc,
all,
}
class FilterDialog extends StatefulWidget {
const FilterDialog({
Key? key,
}) : super(key: key);
#override
State<FilterDialog> createState() => _FilterDialogState();
}
class _FilterDialogState extends State<FilterDialog> {
VoltageMode? selectedMode;
#override
Widget build(BuildContext context) {
return BlocBuilder<MapPreferencesCubit, MapPreferencesState>(
builder: (context, statePreferences) {
final MapPreferencesCubit mapPreferencesCubit =
BlocProvider.of<MapPreferencesCubit>(context);
if (statePreferences is MapPreferencesInitial) {
mapPreferencesCubit.getPreferences();
}
if (statePreferences is MapPreferencesLoaded) {
return BlocBuilder<MapfilterCubit, MapFilterState>(
builder: (context, stateFilter) {
final MapfilterCubit mapFilterCubit =
BlocProvider.of<MapfilterCubit>(context);
if (stateFilter is MapFilterInitial) {
mapFilterCubit.getFilter();
}
if (stateFilter is MapFilterLoaded) {
bool isVoltageAC = stateFilter.mapFilter.voltagePowerAC;
bool isVoltageDC = stateFilter.mapFilter.voltagePowerDC;
bool isVoltageAll = stateFilter.mapFilter.voltagePowerAll;
return SingleChildScrollView(
child: Dialog(
insetPadding: const EdgeInsets.only(
top: 121, left: 24, right: 24, bottom: 60),
child: Container(
decoration: const BoxDecoration(
color: constants.Colors.greyDark,
borderRadius: BorderRadius.all(Radius.circular(24)),
),
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 26, 0, 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [,
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 21),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () => setState(() {
selectedMode = VoltageMode.ac;
}),
child: _buttonVoltage(
'AC', selectedMode == VoltageMode.ac),
),
const SizedBox(width: 16),
GestureDetector(
onTap: () => setState(() {
selectedMode = VoltageMode.dc
}),
child: _buttonVoltage(
'DC', selectedMode == VoltageMode.dc),
),
const SizedBox(width: 16),
GestureDetector(
onTap: () => setState(() {
selectedMode = VoltageMode.all;
}),
child: _buttonVoltage(
'All', selectedMode == VoltageMode.all),
),
],
),
),
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 21),
child: DefaultButtonGlow(
text: 'Done',
onPressed: () {
Navigator.pop(context);;
mapFilterCubit
.setFilter(
MapFilter(
voltagePowerAC:
selectedMode == VoltageMode.ac,
voltagePowerDC:
selectedMode == VoltageMode.dc,
voltagePowerAll:
selectedMode == VoltageMode.all,
),
)
},
),
Widget _buttonVoltage(String nameButton, bool isActive) => Container(
padding: const EdgeInsets.symmetric(vertical: 11),
height: 40,
width: 87,
decoration: BoxDecoration(
color: isActive
? constants.Colors.purpleMain
: constants.Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: isActive ? Colors.transparent : constants.Colors.greyDark,
),
boxShadow: [
BoxShadow(
color: isActive
? constants.Colors.purpleMain.withOpacity(0.34)
: Colors.transparent,
blurRadius: 10,
spreadRadius: 2,
offset: const Offset(0.0, 1.0)),
],
),
alignment: Alignment.center,
child:
Text(nameButton, style: constants.Styles.smallBoldTextStyleWhite),
);
cubit
Future setFilter(MapFilter mapFilter) async {
await _repository.setFilter(mapFilter: mapFilter);
final MapFilter? filter = await _repository.getFilter();
emit(MapFilterLoaded(filter!));
return filter;}
sharedpreferences
Future setFilter({required MapFilter mapFilter}) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString(_filterName, jsonEncode(mapFilter.toJson()));
}

you can read this values from SharedPreferences in initState of page. In this way your default value is ready when page loaded.
then make everything after SingleChildScrollView in separate widget like this:
Widget _buildBody(){
return SingleChildScrollView(
child: Dialog(
...
),
);
}
and pass this widget in your bloc builder after all if statement by default.
then do this:
void initState() {
super.initState();
final SharedPreferences prefs = await SharedPreferences.getInstance();
var result = prefs.readData(_filterName);
if (result != null) {
MapFilter mapFilter = jsonDecode(mapFilter.fromJson(result));
if (mapFilter.voltagePowerAC){
selectedMode = VoltageMode.ac;
}else if (mapFilter.voltagePowerDC){
selectedMode = VoltageMode.dc;
} else {
selectedMode = VoltageMode.all;
}
}
}

Related

Cannot remove strings from list in flutter

I have a page to select images from the gallery and add those urls in a list that will be passed to another page after popping. Everything works fine the first time the user accesses the page, however, when coming back to the Image View with an already populated list (passed as argument), whenever I try to edit/removing elements from it, the list remains the same. But the images I want to delete get removed correctly from the firebase storage.
I will attach the code I am trying to use. I am new to flutter so I will really appreciate your help.
If you have any questions about the code feel free to ask!
import 'dart:convert';
import 'dart:io';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:moneybo/utilities/dialogs/delete_dialog.dart';
import 'package:moneybo/utilities/dialogs/error_dialog.dart';
import 'package:moneybo/utilities/generics/get_arguments.dart';
class ImageView extends StatefulWidget {
const ImageView({super.key});
#override
State<ImageView> createState() => _ImageViewState();
}
class _ImageViewState extends State<ImageView> {
late final ImagePicker _picker;
late final ImageCache _cache;
List<String> imageUrls = [];
List<bool> isImageSelected = [];
List<String> imagesToDelete = [];
String imgs = "";
bool _isSelected = false;
#override
void initState() {
_picker = ImagePicker();
_cache = ImageCache();
super.initState();
}
#override
Widget build(BuildContext context) {
List<String>? newImageList;
final imageList = context.getArgument<String>();
if (imageList != null && imageList.isNotEmpty) {
newImageList = (jsonDecode(imageList) as List<dynamic>).cast<String>();
for (String url in newImageList) {
if (!imageUrls.contains(url)) {
imageUrls.add(url);
}
}
imgs = jsonEncode(imageUrls);
for (String element in imageUrls) {
isImageSelected.add(false);
}
}
return GestureDetector(
onTap: () {
List<bool> newIsImageSelected = [];
for (String element in imageUrls) {
newIsImageSelected.add(false);
}
setState(() {
_isSelected = false;
isImageSelected = newIsImageSelected;
imagesToDelete = [];
});
print(imageUrls);
// print(imgs);
},
child: Scaffold(
appBar: AppBar(
centerTitle: true,
toolbarHeight: 73,
leading: Padding(
padding: const EdgeInsets.only(top: 25, left: 5),
child: TextButton(
child: const Text(
"OK",
style: TextStyle(fontSize: 18, color: Colors.white),
),
onPressed: () => Navigator.pop(context, imgs),
),
),
actions: [
Padding(
padding: const EdgeInsets.only(right: 20, top: 20),
child: isImageSelected.contains(true)
? IconButton(
onPressed: () async {
final shouldDelete = await showDeleteDialog(
context, "Delete this image(s)?");
if (shouldDelete) {
for (String deleteImage in imagesToDelete) {
imageUrls.removeWhere((image) =>
image.hashCode == deleteImage.hashCode);
await FirebaseStorage.instance
.refFromURL(deleteImage)
.delete();
}
imgs = jsonEncode(imageUrls);
List<bool> newIsImageSelected = [];
for (String element in imageUrls) {
newIsImageSelected.add(false);
}
_cache.clear();
_cache.clearLiveImages();
setState(() {
isImageSelected = newIsImageSelected;
imagesToDelete = [];
});
}
},
icon: SizedBox(
height: 25,
child: Image.asset(
"lib/icons/bin.png",
color: Colors.white,
)))
: IconButton(
icon: const Icon(Icons.add_a_photo_rounded),
onPressed: () async {
List<XFile>? images = await _picker.pickMultiImage();
String uniqueFileName =
DateTime.now().microsecondsSinceEpoch.toString();
Reference referenceRoot =
FirebaseStorage.instance.ref();
Reference referenceDirImages =
referenceRoot.child("images");
try {
for (XFile image in images) {
uniqueFileName =
(int.parse(uniqueFileName) + 1).toString();
Reference referenceImageToUpload =
referenceDirImages.child(uniqueFileName);
await referenceImageToUpload
.putFile(File(image.path));
String imageUrl =
await referenceImageToUpload.getDownloadURL();
imageUrls.add(imageUrl);
isImageSelected.add(false);
}
} catch (error) {
if (mounted) {
showErrorDialog(context, "$error");
}
}
imgs = jsonEncode(imageUrls);
setState(() {});
},
),
),
],
title: const Padding(
padding: EdgeInsets.only(top: 30.0),
child: Text(
"Add images",
style: TextStyle(fontSize: 16),
),
),
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[
Color.fromRGBO(24, 92, 92, 1),
Color.fromRGBO(33, 108, 108, 1),
Color.fromRGBO(40, 121, 121, 1),
Color.fromRGBO(48, 136, 136, 1),
Color.fromRGBO(50, 139, 139, 1),
Color.fromRGBO(54, 143, 143, 1),
Color.fromRGBO(57, 145, 145, 1),
]),
),
),
),
body: imageUrls.isNotEmpty
? Padding(
padding: const EdgeInsets.only(top: 20, left: 8, right: 8),
child: GridView.builder(
itemCount: imageUrls.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: imageUrls.length > 1 ? 2 : 1,
mainAxisSpacing: imageUrls.isNotEmpty ? 8 : 0,
crossAxisSpacing: imageUrls.isNotEmpty ? 8 : 0,
),
itemBuilder: (context, index) {
return GestureDetector(
onLongPress: () {
setState(() {
_isSelected = true;
});
},
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: _isSelected
? Colors.green
: const Color.fromRGBO(11, 68, 68, 1),
width: _isSelected ? 4 : 2),
borderRadius:
const BorderRadius.all(Radius.circular(20))),
child: Stack(
children: [
Center(
child: Image.network(
imageUrls[index],
fit: BoxFit.contain,
),
),
_isSelected
? Align(
alignment: Alignment.topRight,
child: Transform.scale(
scale: 1.3,
child: Checkbox(
side: const BorderSide(
width: 2,
color: Color.fromRGBO(
11, 68, 68, 1)),
shape: const CircleBorder(),
value: isImageSelected[index],
onChanged: (bool? value) {
setState(() {
isImageSelected[index] = value!;
});
if (isImageSelected[index]) {
if (imagesToDelete.isNotEmpty) {
imagesToDelete.insert(
index, imageUrls[index]);
} else {
imagesToDelete
.add(imageUrls[index]);
}
} else if (!isImageSelected[
index] &&
imagesToDelete.contains(
imageUrls[index])) {
imagesToDelete
.remove(imageUrls[index]);
} else {
return;
}
print(imagesToDelete);
}),
),
)
: const SizedBox(),
],
),
),
);
},
),
)
: Padding(
padding: const EdgeInsets.only(bottom: 150),
child: Center(
child: SizedBox(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(
Icons.image_not_supported_outlined,
size: 150,
color: Color.fromRGBO(168, 168, 168, 1),
),
SizedBox(height: 25),
Text(
"No images yet",
style: TextStyle(
color: Color.fromRGBO(168, 168, 168, 1),
fontSize: 25,
),
)
]),
)),
),
),
);
}
}

I tried to fetch data from firestore to chip widgets but then show "LateInitializationError". How I solve it?

I tried to fetch data from firestore to chip widgets but then show "LateInitializationError". And also chips should be can multi selection(select many chips). And also how to align 4 chips in a row like this example?I my code I think chips are show like ListView.
error..
I mean like this..
my code..
class uitry extends StatefulWidget {
const uitry({Key? key}) : super(key: key);
#override
State<uitry> createState() => _uitryState();
}
#override
Future<List<Words12>> fetchRecords() async {
var records = await FirebaseFirestore.instance.collection('12words').get();
return mapRecords(records);
}
List<Words12> mapRecords(QuerySnapshot<Map<String, dynamic>> records) {
var _list = records.docs
.map(
(words12) => Words12(
id: words12.id,
wordName: words12['wordName'],
categoryName: words12['categoryName'],
),
)
.toList();
return _list;
}
late int defaultChoiceIndex;
#override
void initState() {
initState();
defaultChoiceIndex = 0;
}
child: SizedBox(
width: width * 0.94,
height: height * 0.30,
child: FutureBuilder<List<Words12>>(
future: fetchRecords(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List<Words12> data = snapshot.data ?? [];
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return (ChoiceChip(
label: Text(data[index].wordName),
selected: defaultChoiceIndex == index,
selectedColor: Colors.deepPurple,
onSelected: (value) {
setState(() {
defaultChoiceIndex =
value ? index : defaultChoiceIndex;
});
},
// backgroundColor: color,
elevation: 1,
padding: const EdgeInsets.symmetric(
horizontal: 5.0),
));
},
);
}
}),
),
#override
void initState() {
initState();
defaultChoiceIndex = 0;
}
Should be:
#override
void initState() {
super.initState();
defaultChoiceIndex = 0;
}
I believe it will initialize your defaultChoiceIndex then.
For the alignment of chips: Wrap your ChoiceChip in the ListView.builder in a Row(), with a mainAxisAlignment of your choosing:
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ChoiceChip(etc.),
],
),
I tried it .Now it working..
code
SizedBox(
width: width * 0.94,
height: height * 0.30,
child: Column(
children: <Widget>[
const SizedBox(height: 16),
Wrap(
children: hobbyList.map(
(hobby) {
bool isSelected = false;
if (selectedHobby!.contains(hobby)) {
isSelected = true;
}
return GestureDetector(
onTap: () {
if (!selectedHobby!.contains(hobby)) {
if (selectedHobby!.length < 50) {
selectedHobby!.add(hobby);
setState(() {});
print(selectedHobby);
}
} else {
selectedHobby!.removeWhere(
(element) => element == hobby);
setState(() {});
print(selectedHobby);
}
},
child: Container(
margin: const EdgeInsets.symmetric(
horizontal: 5, vertical: 4),
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 5, horizontal: 12),
decoration: BoxDecoration(
color: isSelected
? HexColor('#F5F185')
: HexColor('#D9D9D9'),
borderRadius:
BorderRadius.circular(18),
border: Border.all(
color: isSelected
? HexColor('#F5F185')
: HexColor('#D9D9D9'),
width: 2)),
child: Text(
hobby,
style: TextStyle(
color: isSelected
? Colors.black
: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600),
),
),
),
);
},
).toList(),
),
],
),
),
class _uitryState extends State<uitry> {
List<String> hobbyList = [
'Shopping',
'Brunch',
'Music',
'Road Trips',
'Tea',
'Trivia',
'Comedy',
'Clubbing',
'Drinking',
'Wine',
];
List<String>? selectedHobby = [];

Store Image in List<Xfile> from image urls

I have created a variable
List<Xfile> imageList;
using this variable I have showed the selected images in GridView.Builder and uploaded them.
But I want to store those uploaded images in this List to show them again in GridView.Builder.
Means How to store images from imageUrls in List
How can I achieve this?
Follow as follows:
Variables
final picker = ImagePicker();
File? file;
XFile? pickedImage;
bool isLoading = false;
List<File?> fileList = [];
Method to select image from gallery
Future pickImageFromGallery() async {
pickedImage = await picker.pickImage(source: ImageSource.gallery);
setState(() {
file = File(pickedImage!.path);
fileList.add(file);
});
}
And place in gridview as follows:
GridView.builder(
itemCount: fileList.length,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemBuilder: (BuildContext context, int i) {
return Container(
padding: const EdgeInsets.all(10),
child: Stack(
children: <Widget>[
SizedBox(
height: 100,
width: 100,
child: Image.file(File(fileList[i]!.path),fit: BoxFit.cover,),
),
Positioned(
right: 1,
child: GestureDetector(
onTap: () {
setState(() {
dltImages(fileList[i]);
});
},
child: const Icon(Icons.cancel, color: Colors.red),
))
],
),
);
},
),
Find full code at:
https://github.com/nbnD/image_picker_flutter/blob/master/lib/homepage.dart
I do like this if there is multi images upload
class PickImagesPage extends StatefulWidget {
const PickImagesPage({super.key, required this.initialUrls});
final List<String> initialUrls;
#override
State<PickImagesPage> createState() => _PickImagesPageState();
}
class _PickImagesPageState extends State<PickImagesPage> {
#override
void initState() {
urls = widget.initialUrls;
super.initState();
}
List<String> urls = [];
List<File> files = [];
List<String> removedUrls = [];
final Repo repo = Repo();
#override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final style = theme.textTheme;
final scheme = theme.colorScheme;
return LoadingLayer(
child: Scaffold(
bottomNavigationBar: Padding(
padding: const EdgeInsets.fromLTRB(24, 0, 24, 24),
child: ElevatedButton(
onPressed:
files.isNotEmpty || widget.initialUrls.length != urls.length
? () async {
try {
await repo.uploadImages(
files: files,
urls: urls,
removedUrls: removedUrls,
);
Navigator.pop(context);
} catch (e) {
AppSnackbar(context).error(e);
if (kDebugMode) {
print(e);
}
}
}
: null,
child: const Text(Labels.save),
),
),
appBar: AppBar(
title: const Text(
Labels.ambienceImages,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
final List<XFile> pickedFiles = await pickImages();
if (pickedFiles.isNotEmpty) {
setState(() {
files.addAll(pickedFiles.map((e) => File(e.path)));
});
}
},
child: const Icon(Icons.add),
),
body: GridView.count(
padding: const EdgeInsets.all(12),
crossAxisCount: 2,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
children: [
...urls
.map(
(e) => GestureDetector(
onTap: () {
setState(() {
urls.remove(e);
removedUrls.add(e);
});
},
child: Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: scheme.surfaceVariant.withOpacity(0.5),
borderRadius: BorderRadius.circular(20),
image: DecorationImage(
image: NetworkImage(e),
),
),
),
),
)
.toList(),
...files
.map(
(e) => Container(
clipBehavior: Clip.antiAlias,
alignment: Alignment.topRight,
decoration: BoxDecoration(
color: scheme.surfaceVariant.withOpacity(0.5),
borderRadius: BorderRadius.circular(20),
image: DecorationImage(
image: FileImage(e),
),
),
child: SizedBox(
height: 40,
width: 40,
child: RawMaterialButton(
elevation: 0,
focusElevation: 0,
hoverElevation: 0,
shape: const CircleBorder(),
fillColor: theme.cardColor.withOpacity(0.5),
onPressed: () {
setState(() {
files.remove(e);
});
},
child: const Icon(Icons.remove),
),
),
),
)
.toList(),
GestureDetector(
onTap: () async {
final List<XFile> pickedFiles = await pickImages();
if (pickedFiles.isNotEmpty) {
setState(() {
files.addAll(pickedFiles.map((e) => File(e.path)));
});
}
},
child: Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: scheme.surfaceVariant.withOpacity(0.5),
borderRadius: BorderRadius.circular(20),
),
child: Stack(
children: const [
Center(
child: Icon(Icons.add),
),
PickImageLabel(),
],
),
),
),
],
),
),
);
}
}
class Repo {
Future<void> uploadImages(
{required List<String> urls,
required List<File> files,
required List<String> removedUrls}) async {
List<String> newUrls = [];
for (var file in files) {
final url = await upload(file);
newUrls.add(url);
}
for (var url in removedUrls) {
await deleteImage(url);
}
await saveImages(urls + newUrls);
}
}

How to return the PageView to its initial state in Flutter

I am making a quiz app and at first everything works fine, but when I do a quiz the first time, it does the correct or incorrect answer check perfectly.
But when I go back to quiz without restarting the app just navigating from one page to another the PageView does not reset its state again.
Before taking the quiz
enter image description here
After I do the quiz and I want to do it again without restart the app, I get the checked answers.
enter image description here
How to return the PageView to its initial state without restart the app
Here is my code:
import 'package:flutter/material.dart';
import 'package:quizapp/src/models/quiz_model.dart';
import 'package:quizapp/src/screens/result_screen.dart';
class QuizScreen extends StatefulWidget {
const QuizScreen({Key? key}) : super(key: key);
#override
State<QuizScreen> createState() => _QuizScreenState();
}
class _QuizScreenState extends State<QuizScreen> {
int _questionNumber = 1;
late PageController _controller;
int _score = 0;
#override
void initState() {
_controller = PageController(initialPage: 0);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: PageView.builder(
physics: const NeverScrollableScrollPhysics(),
controller: _controller,
itemCount: questions.length,
itemBuilder: (context, index) {
final _question = questions[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 16,
),
Text(
_question.text,
style: const TextStyle(fontSize: 22),
),
const SizedBox(
height: 16,
),
Expanded(
child: SingleChildScrollView(
child: Column(
children: _question.options
.map((option) => GestureDetector(
onTap: () {
Future.delayed(
const Duration(milliseconds: 250),
() {
if (_questionNumber <
questions.length) {
_controller.nextPage(
duration: const Duration(
milliseconds: 250),
curve: Curves.easeInExpo);
setState(() {
if (option.isCorrect == true) {
_score++;
}
});
setState(() {
_questionNumber++;
// _isLocked = false;
});
} else {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) =>
ResultScreen(
score: _score),
));
}
});
if (_question.isLocked) {
return;
} else {
setState(() {
_question.isLocked = true;
_question.selectedOption = option;
});
}
},
child: Container(
height: 50,
padding: const EdgeInsets.all(12),
margin: const EdgeInsets.symmetric(
vertical: 8),
decoration: BoxDecoration(
color: const Color(0xFF6949FD),
borderRadius:
BorderRadius.circular(16),
border: Border.all(
color: getColorForOption(
option, _question))),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
option.text,
style: const TextStyle(
fontSize: 18,
color: Colors.white),
),
const SizedBox(width: 10),
getIconForOption(option, _question)
],
),
),
))
.toList(),
)))
]);
},
)),
),
],
),
));
}
Color getColorForOption(Option option, Question _question) {
final isSelected = option == _question.selectedOption;
if (_question.isLocked) {
if (isSelected) {
return option.isCorrect ? Colors.green : Colors.red;
} else if (option.isCorrect) {
return Colors.green;
}
}
return const Color(0xFF6949FD);
}
Widget getIconForOption(Option option, Question _question) {
final isSelected = option == _question.selectedOption;
if (_question.isLocked) {
if (isSelected) {
return option.isCorrect
? const Icon(Icons.check_circle, color: Colors.green)
: const Icon(Icons.cancel, color: Colors.red);
} else if (option.isCorrect) {
return const Icon(Icons.check_circle, color: Colors.green);
}
}
return const SizedBox.shrink();
}
}
An easier way is to restart the app when you go back or press a button. You can wrap Scaffold() with WillPopScope() to restart when you back. You can use this package to restart.
If you need to save the score, you can save it in local storage. Another easy package for this is get_storage.
dependencies:
flutter_phoenix: ^1.1.0
runApp(Phoenix(child: const MyApp()));
WillPopScope(
onWillPop: () async {
Phoenix.rebirth(context);
},
child: Scaffold())

Flutter Stateful Widget used across Multiple Screens getting Rebuilt

ive created the below Multiselect Chip Widget its using Provider and Listening for changes to the list
the widget creates a List of Choice Chips that allows multiple choice chips to be chosen
class MultiSelectChip extends StatefulWidget {
final Function(List<String>) onSelectionChanged;
MultiSelectChip({this.onSelectionChanged});
#override
_MultiSelectChipState createState() => _MultiSelectChipState();
}
class _MultiSelectChipState extends State<MultiSelectChip> {
List<String> selected = List();
List<Clinic> clinicList = List();
#override
void didChangeDependencies() {
final list = Provider.of<ClinicProvider>(context).clinics;
final clinic = Clinic(
id: null,
name: "All Clinics",
city: null,
suburb: null,
postcode: null,
prate: null,
udarate: null,
goal: null,
uid: null);
clinicList.add(clinic);
selected.add(clinicList[0].name);
list.forEach((clinic) => clinicList.add(clinic));
super.didChangeDependencies();
}
_buildList() {
List<Widget> choices = List();
clinicList.forEach((item) {
choices.add(Padding(
padding: const EdgeInsets.only(left: 5.0, right: 5.0),
child: ChoiceChip(
key: Key("${item.name}"),
shape: selected.contains(item.name)
? RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(0),
),
)
: RoundedRectangleBorder(
side: BorderSide(
color: Color.fromRGBO(46, 54, 143, 1), width: 1.0),
borderRadius: BorderRadius.circular(0.0),
),
label: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(item.name),
),
onSelected: (value) {
setState(() {
selected.contains(item.name)
? selected.remove(item.name)
: selected.add(item.name);
widget.onSelectionChanged(selected);
});
},
selected: selected.contains(item.name),
selectedColor: Color.fromRGBO(46, 54, 143, 1),
labelStyle:
selected.contains(item.name) ? kChipActive : kChipInActive,
backgroundColor: Colors.transparent,
),
));
});
return choices;
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 8.0, top: 5.0, bottom: 5.0),
child: SizedBox(
height: 50,
width: double.infinity,
child: ListView(
scrollDirection: Axis.horizontal,
children: _buildList(),
),
),
);
}
}
when i click from this Log screen and goto the NewLog screen and Pop Back to the Log Screen
class LogScreen extends StatefulWidget {
static const String id = 'logscreen';
#override
_LogScreenState createState() => _LogScreenState();
}
class _LogScreenState extends State<LogScreen> {
MonthSelector selectedMonth;
List<String> selectedItems = List();
static DateTime now = DateTime.now();
static DateTime end = DateTime(now.year, now.month + 1, 0);
static DateTime start = DateTime(now.year, now.month, 1);
MonthSelector currentMonth = MonthSelector(
monthName: DateFormat("MMMM").format(now),
monthStart: start,
monthEnd: end);
void refreshData(MonthSelector selector) async {
await Provider.of<LogProvider>(context, listen: false)
.getLogs(selector.monthStart, selector.monthEnd);
await Provider.of<LogProvider>(context, listen: false)
.loadTreatments(selector.monthStart, selector.monthEnd);
}
#override
Widget build(BuildContext context) {
final List<LogSummary> list = Provider.of<LogProvider>(context).summary;
final List<FlSpot> chartData = Provider.of<LogProvider>(context).spots;
return Container(
color: Color.fromRGBO(246, 246, 246, 1),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 1,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 15,
),
RawMaterialButton(
onPressed: () {
Navigator.pushNamed(context, NewLogScreen.id);
},
constraints: BoxConstraints.tight(Size(60, 60)),
child: Icon(
Icons.add,
color: Color.fromRGBO(255, 255, 255, 1),
size: 30,
),
shape: CircleBorder(),
fillColor: Color.fromRGBO(46, 54, 143, 1),
padding: EdgeInsets.all(15.0),
elevation: 1,
),
SizedBox(
height: 10,
),
Text(
'Add log',
style: kAddLogLabel,
)
],
),
),
]),
list.isEmpty || chartData.isEmpty
? Expanded(
child: Center(
child: Text("No Log Data.."),
),
)
: Expanded(
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
height: 150,
alignment: Alignment.center,
child: LineChartWidget(
list: chartData,
isDollar: true,
),
),
SizedBox(
height: 10,
),
MultiSelectChip(
onSelectionChanged: (selectedList) async {
setState(() {
selectedItems = selectedList;
});
await Provider.of<LogProvider>(context, listen: false)
.filterLogList(selectedItems);
},
),
MonthSelect(Color.fromRGBO(246, 246, 246, 1),
onMonthSelectionChanged: (selected) {
setState(() {
selectedMonth = selected;
});
selectedMonth == null
? refreshData(currentMonth)
: refreshData(selectedMonth);
}),
Padding(
padding:
const EdgeInsets.only(top: 10, left: 0, right: 0),
child: Container(
width: double.infinity,
height: 1.0,
color: kDividerColor,
),
),
what i am seeing is the Multiselect Chip has the Same List of Items redrawn/added to the list view 3 times, each time i go into the NewLog screen the list keeps growing
im currently using the same Widget across 4 diffrent screens, but for some reason when i navigate to one of the other screen the list resets and displays the orignal items and the duplicate items dissapear
what can i do to prevent this from redrawing, all the time when navigating off the screen
thanks
Have you tried specifying listen: false in Provider.of() used in didChangeDependencies()? It may solve the issue.
However, there can still be a risk. I doubt initialising something there is safe because didChangeDependencies() is called when/whenever a dependency of the State object changes as written in its document. It'd be safer to do it in initState(), or have it done outside only once and its result passed in to MultiSelectChip.