If I understand your question correctly, let me try to explain this using the most familiar app of all time, the beginning counter app.
This snippet contains a single StatefulWidget that controls its ability to rebuild using its setState method _incrementCounter. So, the value is incremented and the widget is rebuilt whenever the StatefulWidget calls the setState method inside itself.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
A StatefulWidget can fully rebuild itself, and when doing so, it may also rebuild all its children downstream of it in the widget tree (but not always, as const widgets are not rebuilt). To get another widget to rebuild a parent widget (upstream of it in the widget tree), you need to have that StatefulWidget's setState function. This can be done using a callback function. A callback function is made by the parent widget and passed to a child widget. So, in the following example, I have made a StatelessWidget with a button, which controls its parent widget because it calls its parent's callback function; notice that I give:
ExampleStlessWidget(counter: _counter, fx: _incrementCounter),
and not:
ExampleStlessWidget(counter: _counter, fx: _incrementCounter()),
Passing _incrementCounter() with the parenthesis calls it at the moment it is passed, while _incrementCounter allows it to be called downstream in the widget tree.
Use the callback function in the child widget by calling it anywhere (notice the parentheses).
onPressed: () {
fx();
},
Here is the new code
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ExampleStlessWidget(counter: _counter, fx: _incrementCounter),
);
}
}
class ExampleStlessWidget extends StatelessWidget {
const ExampleStlessWidget({
super.key,
required int counter,
required this.fx,
}) : _counter = counter;
final int _counter;
final Function fx;
@override
Widget build(BuildContext context) {
return Column(
children: [
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
ElevatedButton(
onPressed: () {
fx();
},
child: const Text('Click me'),
),
],
);
}
}
A bloc involves an inherited widget, which allows for monitoring the state throughout the widget tree and rebuilds widgets depending on that state. So, a bloc doesn't need a StatefulWidget to change UI. It would help if you did not look at one tool's ability to rebuild widgets as bad or good. It would be best to look at StatefulWidgets and BLoC as different tools for different jobs.
I hope this helps. Happy coding.