flutter_bloc: ^6.1.1 未更改状态

flutter_bloc: ^6.1.1 not changing state

我正在使用 flutter_bloc: ^6.1.1 以及依赖注入 get_it: ^5.0.6。当我向 bloc 提交事件时,状态没有改变。

nav_bar_bloc.dart

import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';

import '../../../../../core/error/failures.dart';
import '../../../../../core/usecases/usecase.dart';
import '../../../domain/entities/fixtures_entities.dart';
import '../../../domain/entities/prediction_entities.dart';
import '../../../domain/usecases/get_fixtures.dart';
import '../../../domain/usecases/get_predictions.dart';

part 'nav_bar_event.dart';
part 'nav_bar_state.dart';

const String SERVER_FAILURE_MESSAGE = 'SERVER FAILURE. TRY AGAIN LATER';
const String CACHE_FAILURE_MESSAGE = 'CACHE FAILURE';

class NavBarBloc extends Bloc<NavBarEvent, NavBarState> {
  final GetBasicFixtures getBasicFixtures;
  final GetPredictions getPredictions;

  NavBarBloc({
    @required this.getBasicFixtures,
    @required this.getPredictions,
  }) : super(NavBarInitialState());

  @override
  Stream<NavBarState> mapEventToState(
    NavBarEvent event,
  ) async* {
    print(event);
    // FIXTURES EVENT
    // yield HomePageLoadingState();
    if (event is HomePageSelectEvent) {
      Either<Failure, List<BasicFixturesEntity>> response =
          await getBasicFixtures(NoParams());
      yield* response.fold((failure) async* {
        yield HomePageLoadFailureState(message: _mapFailureToMessage(failure));
      }, (fixtures) async* {
        print(fixtures[0].away);
        yield HomePageLoadSuccessState(fixtures: fixtures);
      });
    } // GET PREDICTIONS EVENT
    else if (event is PredictionPageSelectEvent) {
      yield PredictionPageLoadingState();
      Either<Failure, List<PredictionEntity>> response =
          await getPredictions(NoParams());
      yield* response.fold((failure) async* {
        yield PredictionPageLoadFailureState(
            message: _mapFailureToMessage(failure));
      }, (predictions) async* {
        yield PredictionPageLoadSuccessState(predictions: predictions);
      });
    } // NEWS EVENT
    else if (event is NewsPageSelectEvent) {
      yield NewsPageLoadingState();
      yield NewsPageLoadSuccessState();
    } // ACCOUNT EVENT
    else if (event is AccountPageSelectEvent) {
      yield AccountPageLoadingState();
      yield AccountPageLoadSuccessState();
    }
  }

  String _mapFailureToMessage(Failure failure) {
    switch (failure.runtimeType) {
      case ServerFailure:
        return SERVER_FAILURE_MESSAGE;
      case CacheFailure:
        return CACHE_FAILURE_MESSAGE;
      default:
        return 'UNEXPECTED ERROR';
    }
  }
}

nav_bar_event.dart

part of 'nav_bar_bloc.dart';

abstract class NavBarEvent extends Equatable {
  const NavBarEvent();

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

class HomePageSelectEvent extends NavBarEvent {}

class PredictionPageSelectEvent extends NavBarEvent {}

class NewsPageSelectEvent extends NavBarEvent {}

class AccountPageSelectEvent extends NavBarEvent {}

nav_bar_state.dart

part of 'nav_bar_bloc.dart';

abstract class NavBarState extends Equatable {
  const NavBarState();

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

class NavBarInitialState extends NavBarState {}

// Initial HOME PAGE [FIXTURES PAGE]
class HomePageLoadingState extends NavBarState {}

class HomePageLoadSuccessState extends NavBarState {
  final List<BasicFixturesEntity> fixtures;

  HomePageLoadSuccessState({@required this.fixtures});
  @override
  List<Object> get props => [fixtures];
}

class HomePageLoadFailureState extends NavBarState {
  final String message;

  HomePageLoadFailureState({@required this.message});

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

// PREDICTION PAGE
class PredictionPageLoadingState extends NavBarState {}

class PredictionPageLoadSuccessState extends NavBarState {
  final List<PredictionEntity> predictions;

  PredictionPageLoadSuccessState({@required this.predictions});

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

class PredictionPageLoadFailureState extends NavBarState {
  final String message;

  PredictionPageLoadFailureState({@required this.message});

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

// NEWS PAGE
class NewsPageLoadingState extends NavBarState {}

class NewsPageLoadSuccessState extends NavBarState {}

class NewsPageLoadFailureState extends NavBarState {}

// ACCOUNT PAGE
class AccountPageLoadingState extends NavBarState {}

class AccountPageLoadSuccessState extends NavBarState {}

class AccountPageLoadailureState extends NavBarState {}

ui.dart

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

import '../../../../injection_containers/injection_container.dart';
import '../bloc/nav_bar_bloc/nav_bar_bloc.dart';
import '../widgets/core/navbar.dart';
import 'account_page/account_page.dart';
import 'fixtures_page/fixtures_page.dart';
import 'news_page/news_page.dart';
import 'prediction_page/prediction_page.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  void initState() {
    super.initState();
    sl<NavBarBloc>().add(HomePageSelectEvent());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: Colors.black,
        bottomSheet: NavBar(),
        body: BlocProvider(
            create: (_) => sl<NavBarBloc>(),
            child:
                // ignore: missing_return
                BlocBuilder<NavBarBloc, NavBarState>(builder: (context, state) {
              print(state);
              // HOME PAGE aka. FIXTURES PAGE
              if (state is HomePageLoadFailureState) {
                return Center(child: Text(state.message));
              } else if (state is HomePageLoadSuccessState) {
                return FixturesPage(fixtures: state.fixtures);
              } else if (state is HomePageLoadingState) {
                return Center(child: CircularProgressIndicator());
              }
              // PREDICTION PAGE
              else if (state is PredictionPageLoadingState) {
                return Center(child: CircularProgressIndicator());
              } else if (state is PredictionPageLoadFailureState) {
                return Center(child: Text(state.message));
              } else if (state is PredictionPageLoadSuccessState) {
                return PredictionPage(predictions: state.predictions);
              }
              // ACCOUNT PAGE
              else if (state is AccountPageLoadingState) {
                return AccountPage();
              } else if (state is AccountPageLoadSuccessState) {
                return AccountPage();
              } else if (state is AccountPageLoadailureState) {
                return AccountPage();
              }
              // NEWS PAGE
              else if (state is NewsPageLoadingState) {
                return NewsPage();
              } else if (state is NewsPageLoadSuccessState) {
                return NewsPage();
              } else if (state is NewsPageLoadFailureState) {
                return NewsPage();
              } else if (state is NavBarInitialState) {
                return Center(
                    child: Text(
                  "loading",
                  style: TextStyle(color: Colors.white),
                ));
              }
            })));
  }
}

dependency_injection.dart

import 'package:connectivity/connectivity.dart';
import 'package:get_it/get_it.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

import '../core/network/network_info.dart';
import '../features/predictor/data/datasources/local_data_source/fixtures_local_data_source.dart';
import '../features/predictor/data/datasources/local_data_source/prediction_local_data_source.dart';
import '../features/predictor/data/datasources/remote_data_source/fixtures_remote_data_source.dart';
import '../features/predictor/data/datasources/remote_data_source/prediction_remote_data_source.dart';
import '../features/predictor/data/datasources/token_access.dart';
import '../features/predictor/data/repositories/fixtures_repositories_impl.dart';
import '../features/predictor/data/repositories/prediction_repositories_impl.dart';
import '../features/predictor/domain/repositories/fixtures_repositories.dart';
import '../features/predictor/domain/repositories/prediction_repositories.dart';
import '../features/predictor/domain/usecases/get_fixtures.dart';
import '../features/predictor/domain/usecases/get_predictions.dart';
import '../features/predictor/domain/usecases/submit_prediction.dart';
import '../features/predictor/presentation/bloc/nav_bar_bloc/nav_bar_bloc.dart';
import '../features/predictor/presentation/bloc/prediction_submit_bloc/prediction_submit_bloc.dart';

GetIt sl = GetIt.instance;

Future<void> init() async {
  // NavBarBloc
  sl.registerFactory<NavBarBloc>(
      () => NavBarBloc(getBasicFixtures: sl(), getPredictions: sl()));
  // SubmitPredictionBloc
  sl.registerFactory<PredictionSubmitBloc>(
      () => PredictionSubmitBloc(submitPrediction: sl()));

  // Validate token
  sl.registerLazySingleton<ValidateToken>(() => ValidateTokenImpl());

  /// usecase
  ///
  // Fixtures
  sl.registerLazySingleton(() => GetBasicFixtures(repository: sl()));
  // Get-predictions
  sl.registerLazySingleton(() => GetPredictions(repository: sl()));
  // submit-prediction
  sl.registerLazySingleton(() => SubmitPrediction(predictionRepository: sl()));

  /// repository
  ///
  // Fixtures
  sl.registerLazySingleton<BasicFixturesRepository>(() =>
      BasicFixturesRepositoryImpl(
          localDataSource: sl(), networkInfo: sl(), remoteDataSource: sl()));
  // Prediction
  sl.registerLazySingleton<PredictionRepository>(() => PredictionRepositoryImpl(
      localDataSource: sl(), networkInfo: sl(), remoteDataSource: sl()));

  /// Data Source
  /// Remote Date Source
  ///
  // Fixtures
  sl.registerLazySingleton<BasicFixturesRemoteDataSource>(() =>
      BasicFixturesRemoteDataSourceImpl(client: sl(), validateToken: sl()));
  // Prediction
  sl.registerLazySingleton<PredictionsRemoteDataSource>(
      () => PredictionsRemoteDataSourceImpl(client: sl(), validateToken: sl()));

  /// Local Data Source
  ///
  // Fixtures
  sl.registerLazySingleton<BasicFixturesLocalDataSource>(
      () => BasicFixturesLocalDataSourceImpl(sharedPreferences: sl()));
  // Prediction
  sl.registerLazySingleton<PredictionsLocalDataSource>(
      () => PredictionLocalDataSourceImpl(sharedPreferences: sl()));

  // External
  sl.registerLazySingleton<NetworkInfo>(() => NetworkInfoImpl(sl()));
  final sharedPreferences = await SharedPreferences.getInstance();
  sl.registerLazySingleton(() => sharedPreferences);
  sl.registerLazySingleton(() => http.Client());
  sl.registerLazySingleton(() => Connectivity());
}

一切正常,除了 bloc 没有改变它的状态。它停留在初始状态。代码在向服务器发出 API 请求时正常工作。

问题是你注册了NavBarBloc工厂。因此,每当您调用 sl<NavBarBloc>() 时,都会创建一个新的 bloc 实例。

因此,当您调用 sl<NavBarBloc>().add(HomePageSelectEvent()) 时,事件将添加到另一个实例,而不是您传递给 bloc builder 的实例。

更新: 两个简短的例子:

  1. 使用BlocProvider:
...
BlocProvider(
  create: (_) => sl<NavBarBloc>()..add(add(HomePageSelectEvent()),
  ...
)
...
  1. 正在您的小部件中保存实例:
...
NavBarBloc navBarBloc;

@override
void initState() {
  super.initState();
  navBarBloc = sl<NavBarBloc>()..add(HomePageSelectEvent());
}

@override
void dispose() {
  navBarBloc.close();
  super.dispose();
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    backgroundColor: Colors.black,
    bottomSheet: NavBar(),
    body: BlocProvider.value(
      value: navBarBloc,
...