flutter - 如何刷新 Flutter 应用程序仪表板上的计数值? (遵循 BLoC 模式)

标签 flutter bloc flutter-bloc flutter-state

enter image description here

对于如图所示的仪表板设计,我必须显示从 API 获取的各种计数。每个计数都将通过查看标签从不同的 API 获取。

我开发的结构,

  • DashboardScreen
    • CustomBottomMenuOptionWidget (标签)- StatelessWidget
      • MenuCountPage (标签)- StatefulWidget (为了显示计数,我根据标签采用了单独的 MenuCountScreen 小部件及其自己的 eventstatebloc)

当应用程序打开时,一切正常。对于每个不同的菜单选项,我都可以获得每个标签的计数。我的主要问题是用户在应用程序中前进并创建一个新事件,当返回仪表板时,我如何刷新此计数或简单地说我如何将事件添加到 BLoCMenuCountScreen获得更新值?

当前实现:

dashboard.dart( GridView )

GridView.count(
            primary: false,
            padding: const EdgeInsets.all(20),
            crossAxisSpacing: 20,
            mainAxisSpacing: 20,
            crossAxisCount: 2,
            childAspectRatio: 1.3,
            children: <Widget>[
              HomeBottomGridMenuItem(
                label: kMenuLabelCalendar,
                onItemClick: () {
                  _onTapHomeMenuItem(context, kMenuLabelCalendar);
                },
                icon: ICON_CALENDAR,
                itemCountExist: true,
                itemCount: 10,
              ),
              ...other menu item
)

HomeBottomGridMenuItem.dart

import 'package:flutter/material.dart';
import 'package:flutter_app/resources/colors.dart';
import 'package:flutter_app/screens/dashboard/menu_count/menu_count.dart';

class HomeBottomGridMenuItem extends StatelessWidget {
  final String label;
  final String icon;
  final Function onItemClick;
  final bool itemCountExist;
  final int itemCount;

  HomeBottomGridMenuItem({
    this.label,
    this.icon,
    this.onItemClick,
    this.itemCountExist,
    this.itemCount,
  });

  @override
  Widget build(BuildContext context) {
    return Stack(
      fit: StackFit.expand,
      children: <Widget>[
        InkWell(
          onTap: onItemClick,
          splashColor: Colors.black26,
          child: Container(
            padding: EdgeInsets.symmetric(
              horizontal: 16.0,
              vertical: 8.0,
            ),
            decoration: BoxDecoration(
              border: Border.all(
                color: Colors.grey,
              ),
              borderRadius: BorderRadius.all(
                Radius.circular(
                  8.0,
                ),
              ),
            ),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Image.asset(
                  icon,
                  height: 40.0,
                  width: 40.0,
                  color: kColorDashboardMenuItemIcon,
                ),
                Text(
                  label,
                  textAlign: TextAlign.start,
                  style: Theme.of(context).textTheme.headline4.copyWith(
                        fontWeight: FontWeight.w400,
                        color: kColorDashboardMenuItemLabel,
                      ),
                )
              ],
            ),
          ),
        ),
        Positioned(
          top: 0,
          right: 0,
          child: Visibility(
            visible: itemCountExist,
            child: Container(
              padding: EdgeInsets.symmetric(
                horizontal: 14.0,
                vertical: 5.0,
              ),
              decoration: BoxDecoration(
                color: kColorAppPrimaryBlackShade,
                borderRadius: BorderRadius.only(
                  topRight: Radius.circular(
                    5.0,
                  ),
                  bottomLeft: Radius.circular(
                    5.0,
                  ),
                ),
              ),
              child: MenuCountPage(
                label: label,
              ),
            ),
          ),
        ),
      ],
    );
  }
}

MenuCountPage.dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'menu_count.dart';

class MenuCountPage extends StatelessWidget {
  final String label;

  MenuCountPage({
    @required this.label,
  });

  @override
  Widget build(BuildContext context) {
    return BlocProvider<MenuCountBloc>(
      create: (context) {
        return MenuCountBloc(context: context);
      },
      child: MenuCountScreen(
        menuLabel: label,
      ),
    );
  }
}

MenuCountScreen.dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_app/widgets/loader_circular.dart';

import 'menu_count.dart';

class MenuCountScreen extends StatefulWidget {
  final String menuLabel;

  MenuCountScreen({
    @required this.menuLabel,
  });

  @override
  _MenuCountScreenState createState() => _MenuCountScreenState();
}

class _MenuCountScreenState extends State<MenuCountScreen> {
  MenuCountBloc _menuCountBloc;

  @override
  void initState() {
    _menuCountBloc = BlocProvider.of<MenuCountBloc>(context);

    _menuCountBloc.add(
      GetMenuCount(menuLabel: widget.menuLabel),
    );

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: BlocBuilder<MenuCountBloc, MenuCountState>(
        builder: (context, state) {
          if (state is MenuCountSuccess) {
            return Text(
              '${state.count}',
              style: Theme.of(context).textTheme.headline2.copyWith(
                    color: Colors.white,
                  ),
            );
          }
          if (state is MenuCountFail) {
            return Text(
              '0',
              style: Theme.of(context).textTheme.headline2.copyWith(
                    color: Colors.white,
                  ),
            );
          }
          return CircularLoader(
            size: 25,
            strokeWidth: 3,
            color: Colors.white60,
          );
        },
      ),
    );
  }
}

MenuCountEvent.dart

import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';

abstract class MenuCountEvent extends Equatable {
  const MenuCountEvent();
}

class GetMenuCount extends MenuCountEvent {
  final String menuLabel;

  GetMenuCount({@required this.menuLabel});

  @override
  List<Object> get props => [];
}

MenuCountState.dart

import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';

abstract class MenuCountState extends Equatable {
  const MenuCountState();

  @override
  List<Object> get props => [];
}

class MenuCountInitial extends MenuCountState {}

class MenuCountLoading extends MenuCountState {}

class MenuCountSuccess extends MenuCountState {
  final int count;

  MenuCountSuccess({@required this.count});

  @override
  List<Object> get props => [count];
}

class MenuCountFail extends MenuCountState {}

MenuCountBloc.dart

import 'package:chopper/chopper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_app/api/api_service.dart';
import 'package:flutter_app/models/dashboard_count_responses/events_count_response.dart';
import 'package:flutter_app/models/dashboard_count_responses/open_ticket_count_response.dart';
import 'package:flutter_app/models/dashboard_count_responses/properties_count_response.dart';
import 'package:flutter_app/resources/strings.dart';
import 'package:flutter_app/utility/sharedpref_helper.dart';
import 'package:provider/provider.dart';

import 'menu_count.dart';

class MenuCountBloc extends Bloc<MenuCountEvent, MenuCountState> {
  final BuildContext context;

  MenuCountBloc({@required this.context});

  @override
  MenuCountState get initialState => MenuCountInitial();

  @override
  Stream<MenuCountState> mapEventToState(MenuCountEvent event) async* {
    if (event is GetMenuCount) {
      yield MenuCountLoading();
      if (event.menuLabel.length > 0) {
        yield* _getCountValueByLabel(event.menuLabel);
      }
    }
  }

  Stream<MenuCountState> _getCountValueByLabel(String menuLabel) async* {
    switch (menuLabel) {
      case kMenuLabelOpenTickets:
        try {
          final String token = await SharedPreferenceHelper.getToken();
          final Response<OpenTicketCountResponse> apiResponse =
              await Provider.of<ApiService>(context, listen: false)
                  .getOpenTicketCount(token);
          if (apiResponse.isSuccessful) {
            yield MenuCountSuccess(count: apiResponse.body.openTickets);
          } else {
            print('API : Response Failed');
            yield MenuCountSuccess(count: 0);
          }
        } catch (exception) {
          print('Exception: ${exception.toString()}');
          yield MenuCountSuccess(count: 0);
        }
        break;
      case kMenuLabelProperties:
        try {
          final String token = await SharedPreferenceHelper.getToken();
          final Response<PropertiesCountResponse> apiResponse =
              await Provider.of<ApiService>(context, listen: false)
                  .getPropertiesCount(token);
          if (apiResponse.isSuccessful) {
            yield MenuCountSuccess(count: apiResponse.body.properties_active);
          } else {
            print('API : Response Failed');
            yield MenuCountSuccess(count: 0);
          }
        } catch (exception) {
          print('Exception: ${exception.toString()}');
          yield MenuCountSuccess(count: 0);
        }
        break;
      case kMenuLabelCalendar:
        try {
          final String token = await SharedPreferenceHelper.getToken();
          final Response<EventsCountResponse> apiResponse =
              await Provider.of<ApiService>(context, listen: false)
                  .getEventsCount(token);
          if (apiResponse.isSuccessful) {
            yield MenuCountSuccess(count: apiResponse.body.events);
          } else {
            print('API : Response Failed');
            yield MenuCountSuccess(count: 0);
          }
        } catch (exception) {
          print('Exception: ${exception.toString()}');
          yield MenuCountSuccess(count: 0);
        }
        break;
      case kMenuLabelAllTickets:
        try {
          final String token = await SharedPreferenceHelper.getToken();
          final Response<OpenTicketCountResponse> apiResponse =
              await Provider.of<ApiService>(context, listen: false)
                  .getOpenTicketCount(token);
          if (apiResponse.isSuccessful) {
            yield MenuCountSuccess(count: apiResponse.body.tickets);
          } else {
            print('API : Response Failed');
            yield MenuCountSuccess(count: 0);
          }
        } catch (exception) {
          print('Exception: ${exception.toString()}');
          yield MenuCountSuccess(count: 0);
        }
        break;
      default:
        yield MenuCountSuccess(count: 0);
    }
  }
}

我已经尝试过的事情:

  1. 提供 BLoC来自父级(来自“仪表板”)并且当用户返回仪表板时尝试添加事件以获取更新计数。没用。 (如果是因为我有不同的基于标签的 API 调用并且每个计数都与其自己的 MenuCountBloc 实例相关联 - 如果我错了请清除我)

  2. 尝试拍摄 bool值并将其传递给 MenuCountScreen来自 Dashboard当用户返回仪表板时,更新 bool 的值认为它会刷新并再次调用事件但没有奏效。

  3. 除了尝试过的选项 1,取 4 个不同的 intBLoC 中存储 4 个不同计数的参数以及 MenuCountState .以为它会为 BLoC 存储 4 个值我从 Dashboard 提供的.但没有成功。

    我想知道,我的实现方式是否正确以及我尝试过的事情。也是一种可能的解决方案,可以在我遇到困难时完成我的任务。

已编辑: 我在此处推送的示例项目:GitHub

最佳答案

所以基本上你是在说'当你回到旧屏幕时如何更新值?'

与 BLoC 无关,但与Navigators 相关。

如果您注意到,您会看到每个 Navigator.push 函数都返回一个 future 。 基本上您可以等待直到您进入下一个屏幕。

当您从下一个屏幕弹出时,您可以来到第一个屏幕,您的 Navigator.push 会告诉您下一个屏幕已关闭。

让我们看一些代码。

class _AState extends State<A> {
  void gotoB() async {
    await Navigator.push(context, MaterialPageRoute(builder: (context) => B()));
    refreshScreen();
  }

  void refreshScreen() {
    // Call BLoC functions to update the screen
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

class B extends StatefulWidget {
  @override
  _BState createState() => _BState();
}

class _BState extends State<B> {
  completeWork() {
    Navigator.pop(context);
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

A类我要去B类。我在Navigator前面使用await等待我从B回来。

当我在执行 Navigator.pop() 时从 B 返回到 A。 来到这里后,我正在刷新我的屏幕 A

关于flutter - 如何刷新 Flutter 应用程序仪表板上的计数值? (遵循 BLoC 模式),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61889399/

相关文章:

android - Flutter - 在每次应用重启后保持变量的值

Flutter Bloc : BlocBuilder not getting called after an update, ListView 仍然显示旧数据

flutter - 在 dispose 方法中访问 BlocProvider.of<Bloc>(context)

flutter - 'ImagePickerCubit' 不符合类型参数 'BlocBase<ImagePickerState>' 的绑定(bind) 'B'

flutter - 如何在Firebase Phone Auth中使用Flutter Bloc

flutter - 在 Flutter 中将焦点从一个文本字段更改为下一个文本字段

android - Flutter audioplayers : How to handle PlatformException(AndroidAudioError, 标有@UiThread的方法必须在主线程执行)

dart - Flutter:通过导航加载页面时,Observable.combineLatest2 不进行流式传输

flutter - 如何为 'onEvent' 的事件实现去抖动器?

flutter - 使用不包含FicheMvtBloc类型的Bloc的上下文调用的BlocProvider.of()