J_Strauton
J_Strauton

Reputation: 2418

How to directly add bloc to the view widget

I have a screen that creates a widget.

How can I add a bloc to my widget?

class UserView extends StatelessWidget {
   final AnimationController aController;
   final Animation animation;

   @override
   Widget build(BuildContext context) {
     // Add Scafold here?
      return AnimationBuilder(
         animation: aController;
         builder: (BuildContext context, Widget child) {
            ...
         },
      );
   }
}

The bloc

class UserBloc extends Bloc<UserEvent, UserState> {
    final UserRepo userRepo;
    UserBloc({@required this.userRepo}) : assert(userRepo != null);
}

If I add a Scaffold() then I get an error saying "object was given an infinite size during layout".

I am using this https://bloclibrary.dev/#/ for bloc.

I can show more code if necessary, I am trying to keep it light for easy reading. Please ask and I can add more.


App

void main() async {
  final UserRepo userRepo = UserRepo();
  BlocSupervisor.delegate = SimpleBlocDelegate();

  WidgetsFlutterBinding.ensureInitialized();
  await SystemChrome.setPreferredOrientations(<DeviceOrientation>[
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown
  ]).then((_) => runApp(MultiBlocProvider(
        providers: [
          BlocProvider<UserBloc>(
            create: (context) => UserBloc(userRepo: userRepo),
          )
        ],
        child: MyApp(),
      )));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      title: 'Test App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        textTheme: AppTheme.textTheme,
        platform: TargetPlatform.iOS,
      ),
      home: HomeScreen(),
    );
  }
}

Home Screen

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen>
    with TickerProviderStateMixin {
  AnimationController animationController;

  Widget tabBody = Container(
    color: AppTheme.background,
  );

  @override
  void initState() {
    animationController = AnimationController(
        duration: const Duration(milliseconds: 800), vsync: this);
    tabBody = DashboardScreen(animationController: animationController);
    super.initState();
  }

  @override
  void dispose() {
    animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: AppTheme.background,
      child: Scaffold(
        backgroundColor: Colors.transparent,
        body: FutureBuilder<bool>(
          future: getData(),
          builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
            if (!snapshot.hasData) {
              return const SizedBox();
            } else {
              return Stack(
                children: <Widget>[
                  tabBody
                ],
              );
            }
          },
        ),
      ),
    );
  }
}

Dashboard

class DashboardScreen extends StatefulWidget {
  const DashboardScreen({Key key, this.animationController}) : super(key: key);

  final AnimationController animationController;
  @override
  _DashboardScreenState createState() => _DashboardScreenState();
}

class _DashboardScreenState extends State<DashboardScreen>
    with TickerProviderStateMixin {
  Animation<double> topBarAnimation;

  List<Widget> listViews = <Widget>[];
  final ScrollController scrollController = ScrollController();
  double topBarOpacity = 0.0;

  @override
  void initState() {
    listViews.add(
      UserView(
        animation: Tween<double>(begin: 0.0, end: 1.0).animate(CurvedAnimation(
            parent: widget.animationController,
            curve:
                Interval((1 / count) * 1, 1.0, curve: Curves.fastOutSlowIn))),
        animationController: widget.animationController,
      ),
    );
    super.initState();
  }

}

Upvotes: 1

Views: 486

Answers (1)

HII
HII

Reputation: 4109

I will suppose that UserBloc must be available to the whole app, if not, just change the level of the provider below to be just above the widgets it should cover:

Here you provide the bloc to be above MaterialApp widget to be order to use it later in any descendant of this widget:(inside App file)

 return BlocProvider(
    create: (_)=>UserBloc(userRepo:UserRep()),
    child: MaterialApp(
      title: 'Test App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        textTheme: AppTheme.textTheme,
        platform: TargetPlatform.iOS,
      ),
      home: HomeScreen(),
    ),
  );

now if you want to use your bloc to emit events and listen to states in any descendant widget of MaterialApp, you just wrap that widget with a BlocListener or BlocConsumer or BlocBuilder (see difference between them here):

I will suppose you want to do that in HomeScreen:

class _HomeScreenState extends State<HomeScreen>
    with TickerProviderStateMixin {
  AnimationController animationController;

  Widget tabBody = Container(
    color: AppTheme.background,
  );

  @override
  void initState() {
    animationController = AnimationController(
        duration: const Duration(milliseconds: 800), vsync: this);
    tabBody = DashboardScreen(animationController: animationController);
    super.initState();
  }

  @override
  void dispose() {
    animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: AppTheme.background,
      child: Scaffold(
        backgroundColor: Colors.transparent,
        body: FutureBuilder<bool>(
          future: getData(),
          builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
            if (!snapshot.hasData) {
              return const SizedBox();
            } else {
              return Stack(
                children: <Widget>[
                  //tabBody
                  //Don't save widgets as fields, just create them on the fly
                  BlocBuilder<UserBloc,UserState>(
                    builder: (ctx,state){
                      //return widget that depends on state and which should rebuild when state changes
                      return DashboardScreen(animationController: animationController);
                    },
                  )
                ],
              );
            }
          },
        ),
      ),
    );
  }
}

and that's it.

Check the link above for more documentation.

Upvotes: 1

Related Questions