flutter 如何使BLoC示例持久化?

jjjwad0x  于 2023-02-05  发布在  Flutter
关注(0)|答案(1)|浏览(162)

我正在使用电影数据库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,
  );
}

电影详情页面代码:

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,
              ),
            );
      }
    ),
  );
}

我正在从MovieListPage向MovieDetailsPage传递一个BLoC示例,因为它保存了要显示的电影的细节。它在某个时候工作正常,但是当我返回MovieListPage时,我无法选择一个新的电影来显示细节。我如何才能使BLoC示例在导航过程中持久化?

balp4ylt

balp4ylt1#

在你的集团结束后创建你的集团的形象

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或者任何你想要的方法在代码的任何地方访问它。

相关问题