felangel / bloc

A predictable state management library that helps implement the BLoC design pattern
https://bloclibrary.dev
MIT License
11.79k stars 3.39k forks source link

Page view inside tab with bloc state change #2118

Closed rubansiva384 closed 3 years ago

rubansiva384 commented 3 years ago

Hi i have the following screen(Stats Monthly Daily) with stateful widget with TickerProviderStateMixin to use the pageview and a list. Pageview swipe left and right works fine. Whenever user changes the tab (lets say Day Month Year). I need to refresh the page with new content from sqliteDb. but its not working that way, showing the same view(expect Month chart. result Day chart). If i debug, create method never called or bloc provider returning the old bloc i think.

APPBAR ======= TabBar( onTap: (i) { event = ChartTryEventMonth(); context.read().add(event); }

PAGE BLOC =========

@override Stream mapEventToState( ChartTryEvent event, ) async* { if(event is ChartTryEventMonth){ yield ChartTryState(type: "MONTH"); }

PAGE VIEW BUILD() ======== BlocBuilder<ChartTryBloc , ChartTryState>(builder: (context , state){ return Container( height: 100, width: 200, child: PageView.builder( controller: pageController, itemBuilder: (context , index){ return Container( child: Center( child: BlocProvider(lazy : false , create: () => SimpleCompBloc()..add(SceLoad(type: state.type)), child: SingleChartView(),), ), ); }, itemCount: 100, ), ); })

class SingleChartView extends StatelessWidget { @override Widget build(BuildContext context) { return BlocBuilder<SimpleCompBloc, SimpleCompState>(builder: (context , state){ if(state.list == null){ return Container( child: Text("Loading"), ); } else if(state.list != null){ return Container( child: Text("${state.type}${state.list.length}"), ); } return CircularProgressIndicator(); }); } }

class SimpleCompBloc extends Bloc<SimpleCompEvent, SimpleCompState> { SimpleCompBloc() : super(SimpleCompState());

@override Stream mapEventToState( SimpleCompEvent event, ) async* { if(event is SceLoad){ // sleep(Duration(seconds: 3)); yield SimpleCompState(); await Future.delayed(Duration(seconds: 3)); final list= List(); list.add("raja"); yield SimpleCompState(list: list , type : event.type ); } } }

felangel commented 3 years ago

Hi @rubansiva384 👋 Thanks for opening an issue!

Can you please provide a link to a complete reproduction sample? It would be much easier for me to help/provide suggestions if I am able to reproduce the issue locally, thanks! 🙏

rubansiva384 commented 3 years ago

https://github.com/rubansiva384/expense_manager

This is just a learning project.This may have unused files please skip that @felangel

felangel commented 3 years ago

Hi @rubansiva384 sorry for the delayed response! I just took a look and it's because BlocProvider with a create will not re-create the bloc instance. The behavior you wanted can be achieved by using a StatefulWidget to manage the bloc and using BlocProvider.value to provide the existing instance.

import 'package:expense_manager/chart_try/chart_try_bloc.dart';
import 'package:expense_manager/chart_try/single_comp/simple_comp_bloc.dart';
import 'package:expense_manager/chart_try/single_comp/single_chart_view.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class ChartView extends StatefulWidget {
  @override
  _ChartViewState createState() => _ChartViewState();
}

class _ChartViewState extends State<ChartView> {
  final initPage = 99;

  final PageController _pageController = PageController(initialPage: 99);
  final SimpleCompBloc _simpleCompBloc = SimpleCompBloc();

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

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<ChartTryBloc, ChartTryState>(
      builder: (context, state) {
        return Container(
          height: 100,
          width: 200,
          child: PageView.builder(
            controller: _pageController,
            itemBuilder: (context, index) {
              return Container(
                margin: EdgeInsets.all(10.0),
                decoration: BoxDecoration(color: Colors.red),
                child: Center(
                  child: BlocProvider.value(
                    value: _simpleCompBloc..add(SceLoad(type: state.type)),
                    child: SingleChartView(),
                  ),
                ),
              );
            },
            itemCount: 100,
          ),
        );
      },
    );
  }
}

Hope that helps! Closing for now but feel free to comment with additional questions and I'm happy to continue the conversation 👍

rubansiva384 commented 3 years ago

@felangel In your approach Using a single bloc changes the current screen status (All Views state). Lets say the following scenario, i. Day 1 ii. swiping half left and holding the page without releasing the touch call the itembuilder iii. It changes the same day data (Page 1 and Page 2) (i think the same bloc ), instead the new screen state.

Use your same code try the above approach. ( i hope you can reproduce the same ).

tawasoli369 commented 2 years ago

Hello, do you have any training for controlling PageView and footnotes by Bloc Pattern Design?

Obada2020 commented 4 months ago

hello , if i need to access pageController from another page , how i can do that with bloc ?