https://i.stack.imgur.com/qYUwW.png
I am working on an edit screen for editing an existing habit.
A habit has a color and I need to pass the old or previous color of the current habit to be edited to a color_selector
modal sheet such that if the habit’s color is purple, once I click this widget, purple should be pre - selected.
How can I achieve this.
Here’s the controller code for selecting the colors when creating a new habit.
/// Notifier class for exposing a habit's color state.
@riverpod
class SelectColorNotifier extends _$SelectColorNotifier {
@override
// use the last selected color when re-opening the color modal.
int build() => ref.watch(newColorProvider);
void updateColor(int selectedColor) {
if (selectedColor == state) return;
state = selectedColor;
}
}
/// Notifier class for keeping track of the selected color
/// from `selectColorNotifierProvider`.
///
/// Used to set the selected color from the color modal only when the save icon
/// is pressed.
@riverpod
class NewColor extends _$NewColor {
@override
int build() => habitColors.first;
void updateNewColor(int newColor) {
if (newColor == state) return;
state = newColor;
}
}
The color_selector
widget code:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:habitin/src/common_widgets/rounded_padding.dart';
import 'package:habitin/src/constants/constants.dart';
import 'package:habitin/src/features/add_edit/view/new_habit_controller.dart';
class ColorSelector extends StatelessWidget {
const ColorSelector({super.key});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => showColorModalSheet(context),
child: RoundedPadding(
vertical: 8,
horizontal: 8,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Consumer(
builder: (_, ref, __) {
final selectedColor = ref.watch(newColorProvider);
return RoundedPadding(
vertical: 6,
horizontal: 6,
radius: 14,
color: Theme.of(context).colorScheme.onSecondary,
child: Container(
height: 38,
width: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(selectedColor),
),
),
);
},
),
gapW16,
Text("COLOR", style: Theme.of(context).textTheme.bodySmall)
],
),
),
);
}
Future<void> showColorModalSheet(BuildContext context) {
return showModalBottomSheet(
enableDrag: false,
isScrollControlled: true,
isDismissible: false,
context: context,
builder: (_) => Container(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: const Icon(Icons.close_rounded),
onPressed: () => Navigator.pop(context),
),
Text(
"Select Color",
style: Theme.of(context).textTheme.bodyLarge,
),
Consumer(
builder: (_, ref, __) {
return IconButton(
icon: const Icon(Icons.check_rounded),
onPressed: () {
final selectedColor =
ref.read(selectColorNotifierProvider);
ref
.read(newColorProvider.notifier)
.updateNewColor(selectedColor);
Navigator.pop(context);
},
);
},
),
],
),
gapH30,
Wrap(
spacing: 28.0,
runSpacing: 20.0,
children:
habitColors.map((color) => ColorChip(color: color)).toList(),
),
gapH20,
],
),
),
);
}
}
/// Builds the individual Color Chip widgets used in [ColorSelector].
class ColorChip extends StatelessWidget {
const ColorChip({super.key, required this.color, this.oldColor});
final int color;
final int? oldColor;
@override
Widget build(BuildContext context) {
return Consumer(
builder: (_, ref, __) {
final selectedColor = ref.watch(selectColorNotifierProvider);
return GestureDetector(
onTap: () =>
ref.read(selectColorNotifierProvider.notifier).updateColor(color),
child: Container(
height: 60,
width: 60,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(color),
border: selectedColor == color
? Border.all(
width: 3.0,
color: Theme.of(context).colorScheme.onBackground,
)
: const Border(),
),
child: selectedColor == color
? const Icon(Icons.check_rounded)
: const SizedBox.shrink(),
),
);
},
);
}
}
I have tried the usual way of converting ColorSelector
to a statefulWidget and passing the oldColor
in its initState
but this breaks the rebuild when selecting a new color.
I am also thinking of passing the oldColor
as an argument to the selectedColorNotifierProvider
such that if the user is in Edit Mode, the initial state of this provider should be that old color. This hasn't worked correctly so far.
/// Notifier class for exposing a habit's color state.
@riverpod
class SelectColorNotifier extends _$SelectColorNotifier {
@override
// use the last selected color when re-opening the color modal.
int build() => ref.watch(newColorProvider);
void updateColor(int selectedColor) {
if (selectedColor == state) return;
state = selectedColor;
}
}
/// Notifier class for keeping track of the selected color
/// from `selectColorNotifierProvider`.
///
/// Used to set the selected color from the color modal only when the save icon
/// is pressed.
@riverpod
class NewColor extends _$NewColor {
@override
// set the initial state to be the current habit color.
int build(int? oldColor) => oldColor ?? habitColors.first;
void updateNewColor(int newColor) {
if (newColor == state) return;
state = newColor;
}
}