I have a bloc which emits states that contain some progress value (0-100%). This value is to be displayed with LinearProgressIndicator
. Updates may occur in chunks, meaning progress can jump from, say, 0% to 30% in a single step.
Below is a simple snippet to reproduce this behavior:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class StartProgressEvent {}
class ProgressState {
final double progress;
ProgressState(this.progress);
}
class ProgressBloc extends Bloc<StartProgressEvent, ProgressState> {
ProgressBloc() : super(ProgressState(0)) {
on<StartProgressEvent>(_startProgress);
}
void _startProgress(
StartProgressEvent event,
Emitter<ProgressState> emit,
) async {
emit(ProgressState(0.1));
await Future.delayed(const Duration(seconds: 3));
emit(ProgressState(0.4));
await Future.delayed(const Duration(seconds: 3));
emit(ProgressState(0.7));
await Future.delayed(const Duration(seconds: 3));
emit(ProgressState(1.0));
}
}
void main() {
runApp(const DemoApp());
}
class DemoApp extends StatelessWidget {
const DemoApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: BlocProvider<ProgressBloc>(
create: (context) => ProgressBloc()..add(StartProgressEvent()),
child: BlocBuilder<ProgressBloc, ProgressState>(
builder: (context, state) => LinearProgressIndicator(value: state.progress),
),
),
),
),
);
}
}
This snippet shows the following indicator:
Instead of updating instantly, I want my indicator to animate ongoing changes, i.e. to update smoothly from its previous state to the current one.
I found this answer suggesting that we use TweenAnimationBuilder
to animate LinearProgressIndicator
but it implies that we know its current value which we don't.
In a broader sense this question is not limited to progress indicator. I believe it can be framed this way: how can we animate between two consecutive "states" of a widget (either stateless or stateful) within bloc architecture?
CodePudding user response:
You can try AnimatedFractionallySizedBox
with your duration instead of LinearProgressIndicator
class DemoApp extends StatelessWidget {
const DemoApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BlocProvider<ProgressBloc>(
create: (context) => ProgressBloc()..add(StartProgressEvent()),
child: BlocBuilder<ProgressBloc, ProgressState>(
builder: (context, state) => AnimatedFractionallySizedBox(
duration: Duration(seconds: 3),
alignment: Alignment.centerLeft,
widthFactor: state.progress,
child: Container(
alignment: Alignment.centerLeft,
width: double.infinity,
height: 10,
color: Colors.blue,
),
)
// LinearProgressIndicator(value: state.progress),
),
),
],
),
),
);
}
}