flutter showCupertinoModalPopup (date picker) with bloc state does not work

huangapple go评论70阅读模式
英文:

flutter showCupertinoModalPopup (date picker) with bloc state does not work

问题

I am stuck in a situation where the state is being emitted but the UI is not updating. I have checked the code; it is updating the list.

Cubit class method:

void showDatePicker(context, index) async {
    DateTime date = DateTime.now();
    await showCupertinoModalPopup(
      context: context,
      builder: (BuildContext builder) {
        return Container(
          height: MediaQuery.of(context).copyWith().size.height * 0.25,
          color: Colors.white,
          child: Column(
            children: [
              Expanded(
                child: CupertinoDatePicker(
                  mode: CupertinoDatePickerMode.time,
                  onDateTimeChanged: (value) {
                    date = value;
                  },
                  initialDateTime: DateTime.now(),
                ),
              ),
              CupertinoButton(
                child: const Text('Done'),
                onPressed: () {
                  String formattedTime = DateFormat.jm().format(date);
                  var updatedList = state.listOfDailyRoutineOption;
                  updatedList[index].time = formattedTime;
                  emit(state.copyWith(listOfDailyRoutineOption: updatedList));
                  Navigator.of(context).pop();
                },
              )
            ],
          ),
        );
      },
    );
  }

Cubit state class:

part of 'on_board_cubit.dart';

class OnBoardState extends Equatable {
  const OnBoardState({
    this.currentPage = 0,
    this.listOfDailyRoutineOption = const [],
  });

  final int currentPage;
  final List<DailyRoutineModel> listOfDailyRoutineOption;

  @override
  List<Object> get props => [currentPage, listOfDailyRoutineOption];

  OnBoardState copyWith({
    int? currentPage,
    List<DailyRoutineModel>? listOfDailyRoutineOption,
  }) {
    return OnBoardState(
      currentPage: currentPage ?? this.currentPage,
      listOfDailyRoutineOption:
          listOfDailyRoutineOption ?? this.listOfDailyRoutineOption,
    );
  }
}

UI:

class DailyRouteWidget extends StatefulWidget {
  const DailyRouteWidget({Key? key}) : super(key: key);

  @override
  State<DailyRouteWidget> createState() => _DailyRouteWidgetState();
}

class _DailyRouteWidgetState extends State<DailyRouteWidget> {
  @override
  void initState() {
    BlocProvider.of<OnBoardCubit>(context).onAddDailyRoutine();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    return BlocConsumer<OnBoardCubit, OnBoardState>(
      listener: (context, state) {
        // TODO: implement listener
      },
      builder: (context, state) {
        return Padding(
            padding: const EdgeInsets.only(left: 16.0),
            child: ListView(children: [
              const Text("Daily Routine", style: header3Style),
              const SizedBox(height: 20),
              const Text("At what time do you do the following:", style: label),
              const SizedBox(height: 20),
              Column(
                  children: List.generate(state.listOfDailyRoutineOption.length,
                      (index) {
                return Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(
                      state.listOfDailyRoutineOption[index].title,
                      style: label,
                    ),
                    Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: MyElevatedButton(
                        borderRadius: 50,
                        width: size.width * 0.1,
                        height: size.height * 0.03,
                        buttonColor: btnColor,
                        buttonText: state.listOfDailyRoutineOption[index].time,
                        textStyle: const TextStyle(
                          color: Colors.white,
                          fontWeight: FontWeight.bold,
                          fontSize: 13,
                        ),
                        onPressed: () => BlocProvider.of<OnBoardCubit>(context)
                            .showDatePicker(context, index),
                      ),
                    ),
                  ],
                );
              }))
            ]));
      },
    );
  }
}

The bottom sheet correctly displays the time, and it is set for the daily routine. However, even though it is printed, the UI is not updating. I have also called the emit state in the .then of showCupertinoBottomSheet, but the StatefulBuilder does not work.

UI Screenshots:
UI
Bottom Sheet

英文:

i am stuck in a situtation where the state is being emitted but the UI is not updating i have checked the code it is updating the list.

cubit class method

void showDatePicker(context, index) async {
    DateTime date = DateTime.now();
    await showCupertinoModalPopup(
      context: context,
      builder: (BuildContext builder) {
        return Container(
          height: MediaQuery.of(context).copyWith().size.height * 0.25,
          color: Colors.white,
          child: Column(
            children: [
              Expanded(
                child: CupertinoDatePicker(
                  mode: CupertinoDatePickerMode.time,
                  onDateTimeChanged: (value) {
                    date = value;
                  },
                  initialDateTime: DateTime.now(),
                ),
              ),
              CupertinoButton(
                child: const Text(&#39;Done&#39;),
                onPressed: () {
                  String formattedTime = DateFormat.jm().format(date);
                  var updatedList = state.listOfDailyRoutineOption;
                  updatedList[index].time = formattedTime;
                  emit(state.copyWith(listOfDailyRoutineOption: updatedList));
                  Navigator.of(context).pop();
                },
              )
            ],
          ),
        );
      },
    );
  }

cubit state class

part of &#39;on_board_cubit.dart&#39;;

class OnBoardState extends Equatable {
  const OnBoardState({
    this.currentPage = 0,
    this.listOfDailyRoutineOption = const [],
  });

  final int currentPage;
  final List&lt;DailyRoutineModel&gt; listOfDailyRoutineOption;

  @override
  List&lt;Object&gt; get props =&gt; [currentPage, listOfDailyRoutineOption];

  OnBoardState copyWith({
    int? currentPage,
    List&lt;DailyRoutineModel&gt;? listOfDailyRoutineOption,
  }) {
    return OnBoardState(
      currentPage: currentPage ?? this.currentPage,
      listOfDailyRoutineOption:
          listOfDailyRoutineOption ?? this.listOfDailyRoutineOption,
    );
  }
}

ui

class DailyRouteWidget extends StatefulWidget {
  const DailyRouteWidget({Key? key}) : super(key: key);

  @override
  State&lt;DailyRouteWidget&gt; createState() =&gt; _DailyRouteWidgetState();
}

class _DailyRouteWidgetState extends State&lt;DailyRouteWidget&gt; {
  @override
  void initState() {
    BlocProvider.of&lt;OnBoardCubit&gt;(context).onAddDailyRoutine();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    return BlocConsumer&lt;OnBoardCubit, OnBoardState&gt;(
      listener: (context, state) {
        // TODO: implement listener
      },
      builder: (context, state) {
        return Padding(
            padding: const EdgeInsets.only(left: 16.0),
            child: ListView(children: [
              const Text(&quot;Daily Routine&quot;, style: header3Style),
              const SizedBox(height: 20),
              const Text(&quot;At what time do you do the following:&quot;, style: label),
              const SizedBox(height: 20),
              Column(
                  children: List.generate(state.listOfDailyRoutineOption.length,
                      (index) {
                return Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(
                      state.listOfDailyRoutineOption[index].title,
                      style: label,
                    ),
                    Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: MyElevatedButton(
                        borderRadius: 50,
                        width: size.width * 0.1,
                        height: size.height * 0.03,
                        buttonColor: btnColor,
                        buttonText: state.listOfDailyRoutineOption[index].time,
                        textStyle: const TextStyle(
                          color: Colors.white,
                          fontWeight: FontWeight.bold,
                          fontSize: 13,
                        ),
                        onPressed: () =&gt; BlocProvider.of&lt;OnBoardCubit&gt;(context)
                            .showDatePicker(context, index),
                      ),
                    ),
                  ],
                );
              }))
            ]));
      },
    );
  }
}

The bottom sheet popup correctly the time is set for the daily routine even though it is printed but the UI is not updating. i have also called the emit state in .then of showCupertinoBottomSheet
even though the statefulbuilder does not work

SS of UI
flutter showCupertinoModalPopup (date picker) with bloc state does not work
flutter showCupertinoModalPopup (date picker) with bloc state does not work

答案1

得分: 1

以下是翻译好的部分:

  1. 不建议在Widget外部存储或传递BuildContext的实例,因为如果与其关联的Widget从Widget树中卸载,它可能会变得无效。请查看BuildContext

  2. 您的cubit依赖于Flutter框架(特别是cupertino)。但是cubit不应该知道任何与框架类(Widget、导航器等)有关的信息,因为它仅包含与应用程序或表示层无关的业务逻辑。

  3. 您的cubit现在具有多个职责:它不仅管理状态,还开始负责路由和显示UI(showCupertinoModalPopup)。这违反了单一责任原则

在您的特定情况下,有两种可能的变体:

  1. _DailyRouteWidgetState中直接显示底部表单作为对MyElevatedButton的onPressed事件的反应。
  2. 让您的cubit决定如何对用户的点击事件做出反应。在这种情况下,调用cubit的onShowDatePickerPressed,作为反应只发出DisplayDatePickerDatePickerRequired状态,在_DailyRouteWidgetState内部使用BlocListener监听该状态,并作为对该状态的反应调用showCupertinoModalPopup。这是flutter_bloc库文档中的一个很好的例子。这种实现需要更多的代码,但具有更好的关注点分离,并且允许轻松使用cubit和widget测试覆盖您的逻辑。
英文:

There are several problems with your approach:

  1. It's not recommended to store or pass an instance of BuildContext outside the Widget because it may become invalid
    if the widget it is associated with is unmounted from the widget tree. Check BuildContext

  2. Your cubit has dependency on Flutter framework (cupertino in particular). But cubit shouldn't know about any Framework classes (Widgets, Navigators and etc.) at all as it only contains a business logic not related to application or presentation layers.

  3. Your cubit has now more than one responsibility: it's not only managing states but also start to be responsible for routing and displaying UI (showCupertinoModalPopup). This breaks single responsibility principle

In your particular case there two variants possible:

  1. Display bottom sheet in you _DailyRouteWidgetState directly as a reaction on MyElevatedButton onPressed event.
  2. Let your cubit to decide how to react on user tap event. In that case call cubit onShowDatePickerPressed and as a reaction just emit DisplayDatePicker or DatePickerRequired state, listen that state with BlocListener inside _DailyRouteWidgetState and call showCupertinoModalPopup as a reaction for that state. Here is a good example from flutter_bloc library documentation. This implementation require more code but has a better separation of concerns and allow to easily cover your logic with cubit and widget tests.

huangapple
  • 本文由 发表于 2023年7月18日 12:56:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76709630.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定