Как я могу обновить значение счетчика на панели инструментов приложения Flutter? (Шаблон BLoC следует)

введите описание изображения здесь

Для дизайна панели мониторинга, показанного на изображении, я должен отображать различные счетчики, полученные из API. Каждый счетчик будет извлекаться из разных API при просмотре метки.

Моя развитая структура,

  • DashboardScreen
    • CustomBottomMenuOptionWidget(label) - StatelessWidget
      • MenuCountPage(label) - StatefulWidget (to display count, I've taken separate MenuCountScreen widget with its own event, state & bloc as per label)

Когда приложение открывается, все работает нормально. Для каждого отдельного пункта меню я могу получить счетчики для каждого ярлыка. Моя основная проблема заключается в том, что пользователь движется вперед в приложении и создает новое событие, а когда вернется на панель управления, как я могу обновить эти счетчики или просто сказать, как я могу добавить событие в BLoC из MenuCountScreen, чтобы получить обновленное значение ?.

Текущая реализация:

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 разных int параметра, чтобы сохранить 4 разных счетчика как в BLoC, так и в MenuCountState. Думал, что он сохранит 4 значения для BLoC, которые я предоставил из Dashboard. Но не вышло.

    Я хотел бы знать, верен ли мой способ реализации и что я пробовал. Также возможное решение для достижения моей задачи, в которой я застрял.

EDITED: образец проекта, который я разместил здесь: GitHub


person Purvik Rana    schedule 19.05.2020    source источник
comment
Я открыл запрос на перенос (github.com/Purvik/flutter_dashboard_refresh/pull/1) с предложениями о том, как добиться описанного вами поведения. Надеюсь, это поможет!   -  person Felix Angelov    schedule 22.05.2020
comment
@FelixAngelov Спасибо, что помог мне с этим.   -  person Purvik Rana    schedule 28.05.2020


Ответы (1)


По сути, вы говорите: "Когда вы вернетесь к своему старому экрану, как обновить значения?"

Это не связано с BLoC, но связано с навигаторами.

если вы заметите, то увидите, что каждая функция Navigator.push возвращает будущее. Обычно вы можете подождать, пока не перейдете на следующий экран.

Когда вы открываете следующий экран, вы можете перейти к первому экрану, и ваш 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. И я использую ожидание перед навигатором, чтобы дождаться, пока я вернусь из B.

Из B, когда я выполняю Navigator.pop (), я возвращаюсь к A. После этого я обновляю свой экран A.

person vivek yadav    schedule 21.05.2020
comment
Спасибо за ответ, но это не в моем случае. Я уже следил (сценарий, который находится в вашем ответе) для одного из экранов, где я обновляю список, полученный из API. Если возможно, проверьте репозиторий git, чтобы узнать, чего я хочу достичь. - person Purvik Rana; 21.05.2020