0

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;
  }
}


nonsocchi
  • 84
  • 2
  • 6

1 Answers1

0

Solved this by passing the oldColor to the color providers in the controller class.

The new controller class looks like this:

/// 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.
  // we also need to pass the existing habit color to this provider.
  int build(int? oldColor) => ref.watch(newColorProvider(oldColor));
  

  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 when the save icon
/// is pressed.
@riverpod
class NewColor extends _$NewColor {
  @override
  // pass the existing habit color to the initial state of the provider.
  int build(int? oldColor) => oldColor ?? habitColors.first;
  

  void updateNewColor(int newColor) {
    if (newColor == state) return;
    state = newColor;
  }
}

Then watch this provider in the concerned widgets with: final selectedColor = ref.watch(newColorProvider(oldColor));

oldColor is passed to the widget via the constructor.

class ColorSelector extends ConsumerWidget {
  const ColorSelector({super.key, this.oldColor});
  final int? oldColor;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final selectedColor = ref.watch(newColorProvider(oldColor));
    return /*...*/
}
nonsocchi
  • 84
  • 2
  • 6