-
Notifications
You must be signed in to change notification settings - Fork 29.1k
Description
Problem
On android (and maybe elsewhere), Flutter can load old or stale application code, randomly. This leads to extremely frustrating test and debug cycles.
I have been experiencing this with Flutter, for over a year (since I started working with it), but it's always been hard to catch, and usually related to some runtime errors. It's my top issue with productivity in Flutter, but I've never had clean reproduction steps, it just happens, randomly, and usually at the most in-opportune times.
Today I think I've found a reproduceable way to make it happen when testing the Restoration API. And hopefully this can lead to fixing the issue overall.
2020-12-23_02-58-39.mp4
@29s, 39s, and 50s in this video, you can see Flutter loading the previous version of the application 3 times, even though I have repeatedly hot-reloaded a new view. The text has clearly been set to end-alignment, but somehow it reverts to center.
When I hot reload a 2d time, the new view comes back. When the app is restored, the old view comes back!
This is the stuff of developer nightmare fuel. Somewhere is a cached version of the app, that should definitely not be there.
Please, whatever you have to do, make sure that we never get an old version of the app running.. It might seem like a small issue, but at production, at scale, when you're chasing bugs, and you can never be quite sure if your new code is actually being ran... it's really tough to work with, and mentally draining.
It's exacerbated by the frameworks tendency to throw a ton of assert errors, which generally seem to precede this behavior. This makes it hard to spot on small projects as well, it tends to manifest when you're at scale, and when you're in the middle of crunch time. When you do a full stop and deploy, it goes away, which makes it extremely hard to catch.
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel dev, 1.26.0-1.0.pre, on Microsoft Windows [Version 10.0.19041.630], locale en-US)
[√] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
[√] Chrome - develop for the web
[√] Visual Studio - develop for Windows (Visual Studio Community 2019 16.7.6)
[√] Android Studio (version 4.0)
[√] VS Code (version 1.52.1)
[√] Connected device (3 available)
Repro Steps:
- In Android Developer settings, set to "Don't Keep Activities"
- Install and run the following code
- Make changes to the _RestorableCounterState, and force the app to be reloaded, and observe as it easily gets out of sync.
import 'package:flutter/material.dart';
void main() => runApp(RestorationExampleApp());
class RestorationExampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
restorationScopeId: 'app',
title: 'Restorable Counter',
home: RestorableCounter(restorationId: 'counter'),
);
}
}
class RestorableCounter extends StatefulWidget {
RestorableCounter({Key key, this.restorationId}) : super(key: key);
final String restorationId;
@override
_RestorableCounterState createState() => _RestorableCounterState();
}
class _RestorableCounterState extends State<RestorableCounter> with RestorationMixin {
RestorableInt _counter = RestorableInt(0);
@override
String get restorationId => widget.restorationId;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text('You have pushed the button this many times:'),
Text('${_counter.value}', style: Theme.of(context).textTheme.headline4),
SizedBox(height: 100)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() => _counter.value++),
child: Icon(Icons.add),
),
);
}
@override
void restoreState(RestorationBucket oldBucket, bool initialRestore) => registerForRestoration(_counter, "_counter");
}