簡體   English   中英

如何使 BLoC 實例持久化?

[英]How to make BLoC instance persistent?

我正在使用電影數據庫 API 開發一個應用程序。我想閱讀電影列表,然后選擇一部電影並向用戶顯示詳細信息。 MovieDetailsBloc代碼:

import 'package:bloc/bloc.dart';
import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_recruitment_task/movie_details/models/movie_details.dart';
import 'package:flutter_recruitment_task/services/api_service.dart';
import 'package:stream_transform/stream_transform.dart';

part 'movie_details_event.dart';
part 'movie_details_state.dart';

const throttleDuration = Duration(milliseconds: 100);

EventTransformer<E> throttleDroppable<E>(Duration duration) {
  return (events, mapper) {
    return droppable<E>().call(events.throttle(duration), mapper);
  };
}

class MovieDetailsBloc extends Bloc<MovieDetailsEvent, MovieDetailsState> {
  MovieDetailsBloc({required this.apiService}) : super(MovieDetailsState()) {
    on<MovieSelected>(
      _onMovieSelected,
      transformer: throttleDroppable(throttleDuration),
    );
  }

  final ApiService apiService;

  Future<void> _onMovieSelected(MovieSelected event, Emitter<MovieDetailsState> emit) async {
    try{
      final movie = await _fetchMovieDetails(event.query);

      return emit(state.copyWith(
        status: MovieDetailsStatus.success,
        movie: movie,
      ));
    } catch (_) {
      emit(state.copyWith(status: MovieDetailsStatus.failure));
    }
  }

  Future<MovieDetails> _fetchMovieDetails(String id) async {
    final movie = apiService.fetchMovieDetails(id);
    return movie;
  }

  String shouldIWatchIt() {
    final movie = state.movie;
    if(movie.revenue - movie.budget > 1000000 && DateTime.now().weekday == DateTime.sunday){
      return 'Yes';
    } else {
      return 'No';
    }
  }
}

電影列表頁面代碼:

import 'package:flutter/material.dart';
import 'package:flutter_recruitment_task/movie_details/bloc/movie_details_bloc.dart';
import 'package:flutter_recruitment_task/movies/bloc/movies_bloc.dart';
import 'package:flutter_recruitment_task/movies/models/movie.dart';
import 'package:flutter_recruitment_task/pages/movie_details/movie_details_page.dart';
import 'package:flutter_recruitment_task/pages/movie_list/movie_card.dart';
import 'package:flutter_recruitment_task/pages/movie_list/search_box.dart';
import 'package:flutter_recruitment_task/services/api_service.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class MovieListPage extends StatefulWidget {
  @override
  _MovieListPage createState() => _MovieListPage();
}

class _MovieListPage extends State<MovieListPage> {

  final ApiService apiService = ApiService();
  late final MoviesBloc moviesBloc;
  late final MovieDetailsBloc movieDetailsBloc;

  void _onSearchBoxSubmitted(String text) {
    moviesBloc.add(MoviesFetched(query: text));
  }

  @override
  void initState() {
    moviesBloc = MoviesBloc(apiService: apiService);
    movieDetailsBloc = MovieDetailsBloc(apiService: apiService);
    super.initState();
  }

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

  @override
  Widget build(BuildContext context) => BlocProvider(
    create: (_) => movieDetailsBloc,
    child: Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: Icon(Icons.movie_creation_outlined),
            onPressed: () {
              Navigator.of(context).push(
                  MaterialPageRoute(
                      builder: (_) => MovieDetailsPage(
                        movieDetailsBloc: movieDetailsBloc,
                      )
                  )
              );
            },
          ),
        ],
        title: Text('Movie Browser'),
      ),
      body: BlocProvider(
        create: (_) => moviesBloc,
        child: Column(
          children: <Widget>[
            SearchBox(onSubmitted: _onSearchBoxSubmitted),
            Expanded(child: _buildContent()),
          ],
        ),
      ),
    ),
  );

  Widget _buildContent() => BlocBuilder<MoviesBloc, MoviesState>(
      buildWhen: (previous, current) => previous != current,
      builder: (context, state) {
        switch (state.status) {
          case MoviesStatus.failure:
            return const Center(child: Text('failed to fetch movies'));
          case MoviesStatus.success:
            if (state.movies.isEmpty) {
              return const Center(child: Text('no movies'));
            }
            return _buildMoviesList(state.movies);
          case MoviesStatus.initial:
            return const Center(child: CircularProgressIndicator());
        }
      });

  Widget _buildMoviesList(List<Movie> movies) => ListView.separated(
    separatorBuilder: (context, index) => Container(
      height: 1.0,
      color: Colors.grey.shade300,
    ),
    itemBuilder: (context, index) => BlocBuilder<MovieDetailsBloc, MovieDetailsState>(
        buildWhen: (previous, current) => previous != current,
        builder: (context, state) {
        return MovieCard(
          title: movies[index].title,
          rating: '${(movies[index].voteAverage * 10).toInt()}%',
          color: state.movie.title == movies[index].title ?
          Colors.amberAccent : Colors.white,
          onTap: () {
            movieDetailsBloc.add(MovieSelected(query: movies[index].id.toString()));
          },
        );
      }
    ),
    itemCount: movies.length,
  );
}

MovieDetails頁面代碼:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_recruitment_task/movie_details/bloc/movie_details_bloc.dart';
import 'package:flutter_recruitment_task/movie_details/models/movie_detail_placeholder.dart';

class MovieDetailsPage extends StatefulWidget {

  final MovieDetailsBloc? movieDetailsBloc;

  const MovieDetailsPage({super.key, this.movieDetailsBloc});

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

class _MovieDetailsPageState extends State<MovieDetailsPage> {
  var _details = [];

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) => BlocProvider(
    create: (_) => widget.movieDetailsBloc!,
    child: BlocBuilder<MovieDetailsBloc, MovieDetailsState>(
        buildWhen: (previous, current) => previous != current,
        builder: (context, state) {
        _details = [
          MovieDetailPlaceholder(title: 'Budget', content: '\$ ${state.movie.budget}'),
          MovieDetailPlaceholder(title: 'Revenue', content: '\$ ${state.movie.revenue}'),
          MovieDetailPlaceholder(title: 'Should I watch it today?', content: widget.movieDetailsBloc!.shouldIWatchIt()),
        ];

        return Scaffold(
              appBar: AppBar(
                title: Text(state.movie.title),
              ),
              body: ListView.separated(
                separatorBuilder: (context, index) => Container(
                  height: 1.0,
                  color: Colors.grey.shade300,
                ),
                itemBuilder: (context, index) => Container(
                  padding: EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.stretch,
                    children: [
                      Text(
                        _details[index].title,
                        style: Theme.of(context).textTheme.headline5,
                      ),
                      SizedBox(height: 8.0),
                      Text(
                        _details[index].content,
                        style: Theme.of(context).textTheme.subtitle1,
                      ),
                    ],
                  ),
                ),
                itemCount: _details.length,
              ),
            );
      }
    ),
  );
}

我將 BLoC 的一個實例從 MovieListPage 傳遞到 MovieDetailsPage,因為它包含要顯示的電影的詳細信息。 它有一次工作正常,但是當我 go 回到 MovieListPage 時,我無法選擇一部新電影來顯示詳細信息。 我如何使 BLoC 的實例通過導航持續存在?

在你的集團結束后創建你的集團的實例

class MovieDetailsBloc extends Bloc<MovieDetailsEvent, MovieDetailsState> {
  MovieDetailsBloc({required this.apiService}) : super(MovieDetailsState()) {
    on<MovieSelected>(
      _onMovieSelected,
      transformer: throttleDroppable(throttleDuration),
    );
  }

  final ApiService apiService;

  Future<void> _onMovieSelected(MovieSelected event, Emitter<MovieDetailsState> emit) async {
    try{
      final movie = await _fetchMovieDetails(event.query);

      return emit(state.copyWith(
        status: MovieDetailsStatus.success,
        movie: movie,
      ));
    } catch (_) {
      emit(state.copyWith(status: MovieDetailsStatus.failure));
    }
  }

  Future<MovieDetails> _fetchMovieDetails(String id) async {
    final movie = apiService.fetchMovieDetails(id);
    return movie;
  }

  String shouldIWatchIt() {
    final movie = state.movie;
    if(movie.revenue - movie.budget > 1000000 && DateTime.now().weekday == DateTime.sunday){
      return 'Yes';
    } else {
      return 'No';
    }
  }
}



MovieDetailsBloc movieDetailsBloc = MovieDetailsBloc();

您可以使用movieDetailsBloc.add或任何您想要的代碼在代碼中的任何位置訪問它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM