Created with <3 with dartpad.dev.
Last active
February 5, 2023 15:25
-
-
Save vindolin/e3916b47603988efabd7a08712b98287 to your computer and use it in GitHub Desktop.
Riverpod Animation Problem
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ignore_for_file: avoid_print | |
import 'dart:math'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter_riverpod/flutter_riverpod.dart'; | |
void main() { | |
runApp( | |
const ProviderScope( | |
child: MyApp(), | |
), | |
); | |
} | |
class MyApp extends StatelessWidget { | |
const MyApp({super.key}); | |
// This widget is the root of your application. | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Riverpod + animated CustomPainter', | |
home: const Example3(), | |
theme: ThemeData( | |
colorScheme: ColorScheme.fromSeed( | |
seedColor: Colors.orange, | |
brightness: MediaQueryData.fromWindow(WidgetsBinding.instance.window).platformBrightness, | |
surface: Colors.deepOrange[600], | |
), | |
), | |
); | |
} | |
} | |
class ItemPainter extends CustomPainter { | |
final double value; | |
ItemPainter(this.value); | |
final itemPaint = Paint()..color = Colors.orange; | |
@override | |
void paint(Canvas canvas, Size size) { | |
// draw a circle with a size depending on the value | |
double radius = size.width / 10 * value / 2; | |
canvas.drawCircle( | |
Offset( | |
size.width / 2, | |
size.height / 2, | |
), | |
radius, | |
itemPaint, | |
); | |
} | |
@override | |
bool shouldRepaint(covariant ItemPainter oldDelegate) => oldDelegate.value != value; | |
} | |
CustomPaint itemIcon(double value) { | |
return CustomPaint( | |
painter: ItemPainter(value), | |
size: const Size(40, 40), | |
); | |
} | |
@immutable | |
class Item { | |
const Item({required this.id, required this.value}); | |
final String id; | |
final double value; | |
} | |
// notifier that provides a list of items | |
class ItemsNotifier extends Notifier<List<Item>> { | |
@override | |
List<Item> build() { | |
return [ | |
const Item(id: 'A', value: 1.0), | |
const Item(id: 'B', value: 5.0), | |
const Item(id: 'C', value: 10.0), | |
]; | |
} | |
void randomize(String id) { | |
// replace the state with a new list of items where the value is randomized from 0.0 to 10.0 | |
state = [ | |
for (final item in state) | |
if (item.id == id) Item(id: item.id, value: Random().nextInt(100).toDouble() / 10.0) else item, | |
]; | |
} | |
} | |
class AnimatedItem extends StatefulWidget { | |
final Item item; | |
const AnimatedItem(this.item, {super.key}); | |
@override | |
State<AnimatedItem> createState() => _AnimatedItemState(); | |
} | |
class _AnimatedItemState extends State<AnimatedItem> with SingleTickerProviderStateMixin { | |
late final AnimationController _animationController; | |
late Animation<double> animation; | |
@override | |
void initState() { | |
super.initState(); | |
_animationController = AnimationController( | |
value: widget.item.value, | |
vsync: this, | |
duration: const Duration(milliseconds: 3000), | |
); | |
} | |
@override | |
void dispose() { | |
_animationController.dispose(); | |
super.dispose(); | |
} | |
@override | |
void didUpdateWidget(AnimatedItem oldWidget) { | |
super.didUpdateWidget(oldWidget); | |
if (oldWidget.item.value != widget.item.value) { | |
print('didUpdateWidget: ${oldWidget.item.value} -> ${widget.item.value}'); | |
_animationController.value = oldWidget.item.value / 10; | |
_animationController.animateTo(widget.item.value / 10); | |
} | |
} | |
@override | |
Widget build(BuildContext context) { | |
return AnimatedBuilder( | |
animation: _animationController, | |
builder: (context, child) { | |
return itemIcon((widget.item.value * _animationController.value)); | |
}, | |
); | |
} | |
} | |
final itemsProvider = NotifierProvider<ItemsNotifier, List<Item>>(() => ItemsNotifier()); | |
class Example3 extends ConsumerWidget { | |
const Example3({super.key}); | |
@override | |
Widget build(BuildContext context, WidgetRef ref) { | |
final items = ref.watch(itemsProvider); | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text('Animated CustomPainter Problem'), | |
), | |
// iterate over the item list in ItemsNotifier | |
body: ListView.separated( | |
separatorBuilder: (context, index) => const Divider(), | |
itemCount: items.length, | |
itemBuilder: (context, index) { | |
final item = items.elementAt(index); | |
return ListTile( | |
key: Key(item.id), | |
leading: AnimatedItem(item), | |
title: Text('${item.value}'), | |
); | |
}, | |
), | |
floatingActionButton: Row( | |
mainAxisAlignment: MainAxisAlignment.end, | |
children: [ | |
FloatingActionButton( | |
onPressed: () { | |
ref.read(itemsProvider.notifier).randomize('B'); // randomize the value of the second item | |
}, | |
child: const Icon(Icons.change_circle), | |
), | |
], | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment