5

i have a screen with three widget [widgetA, widgetB, widgetC] and i have a bloc[BlocA] which is responsible for the data fetching and displaying on this screen i have three event [eventA, eventB, eventC] which render the widget [widgetA, widgetB, widgetC] and i have three state [stateA, stateB, stateC] which are responsible for managing state of widget [widgetA, widgetB, widgetC] i have attached all code to reproduce and test the case.

I am only able to display one state and their respective widget at a time whereas i want to display all three state and its widget based on their event. any help would be highly appreciated.

only way i tried to achieve the same is by making separate bloc and event class for each widget, but somehow i am not satisfied with this approach.

what would be the best approach to achieve this use case.

TestScreen

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:locus/blocs/test/testbloc.dart';

class TestScreen extends StatelessWidget {
  const TestScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
        create: (context) => TestBloc()..add(const TestEvent1()),
        child: Scaffold(
          appBar: AppBar(
            title: const Text('Test'),
          ),
          body: Stack(
            children: [
              Builder(builder: (context) {
                return Padding(
                  padding: const EdgeInsets.only(top: 0),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      ElevatedButton(
                          onPressed: () =>
                              context.read<TestBloc>().add(const TestEvent1()),
                          child: const Text("Event1")),
                      const SizedBox(width: 10),
                      ElevatedButton(
                          onPressed: () => context
                              .read<TestBloc>()
                              .add(const TestEvent2(" event 2")),
                          child: const Text("Event2")),
                      const SizedBox(width: 10),
                      ElevatedButton(
                          onPressed: () => context
                              .read<TestBloc>()
                              .add(const TestEvent3(false)),
                          child: const Text("Event3")),
                    ],
                  ),
                );
              }),
              BlocBuilder<TestBloc, TestState>(
                builder: (context, state) {
                  if (state is TestState1) {
                    return const Center(child: Text("I am state 1"));
                  }
                  return const SizedBox.shrink();
                },
              ),
              Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  BlocBuilder<TestBloc, TestState>(
                    builder: (context, state) {
                      if (state is TestState2) {
                        return Center(
                            child: Text("I am state 2 ${state.message}"));
                      }
                      return const SizedBox.shrink();
                    },
                  ),
                  BlocBuilder<TestBloc, TestState>(
                    builder: (context, state) {
                      if (state is TestState3) {
                        return Center(
                            child: Text("I am state 3 ${state.check}"));
                      }
                      return const SizedBox.shrink();
                    },
                  ),
                ],
              ),
            ],
          ),
        ));
  }
}

TestBloc


import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:bloc/bloc.dart';
part 'test_state.dart';
part 'test_event.dart';

class TestBloc extends Bloc<TestEvent, TestState> {
  TestBloc() : super(TestInitializing()) {
    on<TestEvent1>((event, emit) => test1(event, emit));
    on<TestEvent2>((event, emit) => test2(event, emit));
    on<TestEvent3>((event, emit) => test3(event, emit));
  }

  Future test1(TestEvent1 event, Emitter<TestState> emit) async {
    try {
      emit(const TestState1());
    } catch (_) {}
  }

  Future test2(TestEvent2 event, Emitter<TestState> emit) async {
    try {
      emit(const TestState2(message: "Hello"));
    } catch (_) {}
  }

  Future test3(TestEvent3 event, Emitter<TestState> emit) async {
    try {
      emit(const TestState3(check: true));
    } catch (_) {}
  }
}

TestEvent


@immutable
abstract class TestEvent extends Equatable {
  const TestEvent();
}

class TestEvent1 extends TestEvent {
  const TestEvent1();
  @override
  List<Object> get props => [];
}

class TestEvent2 extends TestEvent {
  final String message;
  const TestEvent2(this.message);
  @override
  List<Object> get props => [message];
}

class TestEvent3 extends TestEvent {
  final bool check;
  const TestEvent3(this.check);
  @override
  List<Object> get props => [check];
}

TestState


@immutable
abstract class TestState extends Equatable {
  const TestState();
}

class TestInitializing extends TestState {
  @override
  List<Object> get props => [];
}

class TestState1 extends TestState {
  const TestState1();
  @override
  List<Object?> get props => [];
}

class TestState2 extends TestState {
  final String message;
  const TestState2({
    required this.message,
  });
  @override
  List<Object> get props => [message];
}

class TestState3 extends TestState {
  final bool check;
  const TestState3({
    required this.check,
  });
  @override
  List<Object> get props => [check];
}

testbloc barrel class

export 'test_bloc.dart';
avinash
  • 501
  • 2
  • 8
  • 16

3 Answers3

5

A bloc can only have one state at a time. If you want more states than that you'll have to either maintain a custom internal state mechanism inside TestBloc or create 3 separate TestBlocs and then provide each BlocBuilder with each TestBloc like so:

class TestScreen extends StatelessWidget {
  TestScreen({Key? key}) : super(key: key) {
  }

  final TestBloc bloc1 = TestBloc();
  final TestBloc bloc2 = TestBloc();
  final TestBloc bloc3 = TestBloc();

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
        create: (context) => TestBloc()..add(const TestEvent1()),
        child: Scaffold(
          appBar: AppBar(
            title: const Text('Test'),
          ),
          body: Stack(
            children: [
              Builder(builder: (context) {
                return Padding(
                  padding: const EdgeInsets.only(top: 0),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      ElevatedButton(
                          onPressed: () =>
                              bloc1.add(const TestEvent1()),
                          child: const Text("Event1")),
                      const SizedBox(width: 10),
                      ElevatedButton(
                          onPressed: () => bloc2.add(const TestEvent2(" event 2")),
                          child: const Text("Event2")),
                      const SizedBox(width: 10),
                      ElevatedButton(
                          onPressed: () => bloc3.add(const TestEvent3(false)),
                          child: const Text("Event3")),
                    ],
                  ),
                );
              }),
              BlocBuilder<TestBloc, TestState>(
                bloc: bloc1,
                builder: (context, state) {
                  if (state is TestState1) {
                    return const Center(child: Text("I am state 1"));
                  }
                  return const SizedBox.shrink();
                },
              ),
              Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  BlocBuilder<TestBloc, TestState>(
                    bloc: bloc2,
                    builder: (context, state) {
                      if (state is TestState2) {
                        return Center(
                            child: Text("I am state 2 ${state.message}"));
                      }
                      return const SizedBox.shrink();
                    },
                  ),
                  BlocBuilder<TestBloc, TestState>(
                    bloc: bloc3,
                    builder: (context, state) {
                      if (state is TestState3) {
                        return Center(
                            child: Text("I am state 3 ${state.check}"));
                      }
                      return const SizedBox.shrink();
                    },
                  ),
                ],
              ),
            ],
          ),
        ));
  }
}

However making 3 separate blocs (TestBloc1, TestBloc2, TestBloc3) isn't necessaryly a bad way to go in regards to speration of concerns.

CrenshawDK
  • 317
  • 1
  • 10
  • thanks for your valuable response. your solution will work, to be precise i used the same approach in one of my project, but somehow i don't really like this approach and looking for better approach. – avinash Feb 11 '22 at 16:55
  • @avinash u found any solution ? – rahul shalgar Oct 31 '22 at 13:34
  • @rahulshalgar yes, I found the solutions.Actually there are couple of ways of achieving multiple state from the same bloc.You can use buildwhen property of bloc builder to decide when to build the widget or you can use copy of bloc to emit different states and managing the state based on bloc you are emitting the state. Like if you have one bloc named testbloc with multiple state then you can define multiple copies of bloc using bloc provider and in bloc builder you can reference the bloc.Catch is that your bloc can emit one state at one time and based on your requirement you can manage state. – avinash Nov 01 '22 at 14:14
1

I'm pretty late to the party but I've implemented a package that does exactly what you want !

Here you go : https://pub.dev/packages/multi_state_bloc

  • 1
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/32665078) – Dani3le_ Sep 14 '22 at 10:20
1

You can try this code:

buildWhen: (previous, current) => current is TestState1 && previous != current.
DuocNP
  • 34
  • 4