I am watching a tutorial and it is clear that BLoc does not work with anonymous routes as, apparently, they do not share a context. However, my simple app works with anonymous routes.
It is a simple counter app that has 2 screens and the user can manipulate the counter on both of them. In the tutorial when he runs this app and navigates to the second screen , the app crashes , but mine does not and works fine.
Here is the code :
main.dart:
import 'package:counter_bloc_test/logic/cubits/counter_cubit.dart';
import 'package:counter_bloc_test/frontend/screens/home_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return BlocProvider<CounterCubit>(
create: (context) => CounterCubit(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const HomeScreen(title: 'Flutter Demo Home Page', color: Colors.blue,),
),
);
}
}
homescreen.dart:
import 'package:flutter/material.dart';
import 'package:counter_bloc_test/logic/cubits/counter_cubit.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:counter_bloc_test/frontend/screens/second_screen.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key, required this.title , required this.color});
final String title;
final Color color;
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
BlocConsumer<CounterCubit, CounterState>(
listener: (context, state) {
if(state.wasIncremented){
ScaffoldMessenger.of(context).removeCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Incremented"),
)
);
}else{
ScaffoldMessenger.of(context).removeCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Decremented"),
)
);
}
},
builder: (context, state) {
return Text(
state.counterValue.toString(),
style: Theme.of(context).textTheme.headlineMedium,
);
},
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
FloatingActionButton(
heroTag: "btn1",
onPressed: () {
BlocProvider.of<CounterCubit>(context).decrement();
},
tooltip: 'Decrement',
child: const Icon(Icons.remove),
),
FloatingActionButton(
heroTag: "btn2",
onPressed: () {
BlocProvider.of<CounterCubit>(context).increment();
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
],
),
const SizedBox(height: 24,),
MaterialButton(
color: widget.color,
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const SecondScreen(
title: "Second Screen",
color: Colors.redAccent,
)
)
);
},
child: const Text('Go to Second Page'),
)
],
),
),
);
}
}
second_screen.dart
import 'package:flutter/material.dart';
import 'package:counter_bloc_test/logic/cubits/counter_cubit.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class SecondScreen extends StatefulWidget {
const SecondScreen({super.key, required this.title , required this.color});
final String title;
final Color color;
@override
State<SecondScreen> createState() => _SecondScreenState();
}
class _SecondScreenState extends State<SecondScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.redAccent,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
BlocConsumer<CounterCubit, CounterState>(
listener: (context, state) {
if(state.wasIncremented){
ScaffoldMessenger.of(context).removeCurrentMaterialBanner();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Incremented"),
)
);
}else{
ScaffoldMessenger.of(context).removeCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Decremented"),
)
);
}
},
builder: (context, state) {
return Text(
state.counterValue.toString(),
style: Theme.of(context).textTheme.headlineMedium,
);
},
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
FloatingActionButton(
heroTag: null,
onPressed: () {
BlocProvider.of<CounterCubit>(context).decrement();
},
tooltip: 'Decrement',
child: const Icon(Icons.remove),
),
FloatingActionButton(
heroTag: null,
onPressed: () {
BlocProvider.of<CounterCubit>(context).increment();
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
],
),
const SizedBox(height: 24,),
MaterialButton(
color: widget.color,
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Go to First Page'),
)
],
),
),
);
}
}
I expected the app to throw an error, however the state is preserved and it works fine. I am using bloc 8.1 and Flutter 3.10
The difference is with the initial provider you are using.
By wrapping your MaterialApp
with a BlocProvider
you are making it available globally throughout your application.
You are correct by expecting an error in the case the provider is located in your HomeScreen
.