#flutter #flutter-animation
#флаттер #flutter-анимация
Вопрос:
Следующий пример кода делает что-то неожиданное:
import 'package:characters/characters.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
print(' *** _MyHomePageState:_incrementCounter - ${this.hashCode}');
setState(() {
_counter ;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Animated Text Kit Issue 168')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedTextKit(
animatedText: TypewriterAnimatedText(
'Animated Text',
textStyle: const TextStyle(
fontSize: 45.0,
fontWeight: FontWeight.w900,
color: Colors.pink,
),
),
),
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
/// Animated Text that displays a [Text] element as if it is being typed one
/// character at a time.
class TypewriterAnimatedText {
/// Text for [Text] widget.
final String text;
/// [TextStyle] property for [Text] widget.
final TextStyle textStyle;
/// The [Duration] of the delay between the apparition of each characters
///
/// By default it is set to 30 milliseconds.
final Duration speed;
/// The Duration for the Animation Controller.
final Duration duration;
/// Same as [text] but as [Characters].
///
/// Need to use character length, not String length, to propertly support
/// Unicode and Emojis.
final Characters textCharacters;
TypewriterAnimatedText(
this.text, {
@required this.textStyle,
this.speed = const Duration(milliseconds: 30),
}) : assert(null != speed),
textCharacters = text.characters,
duration = speed * (text.characters.length);
Animation<int> _typewriterText;
void initAnimation(AnimationController controller) {
print(' >>> TypewriterAnimatedText:initAnimation - ${this.hashCode}');
_typewriterText = StepTween(
begin: 0,
end: textCharacters.length,
).animate(controller);
}
/// Widget showing partial text
Widget animatedBuilder(BuildContext context, Widget child) {
print(' >>> TypewriterAnimatedText:animatedBuilder - ${this.hashCode}');
final typewriterValue = _typewriterText.value;
final visibleString = '${textCharacters.take(typewriterValue)}_';
return Text(visibleString, style: textStyle);
}
}
/// Base class for Animated Text widgets.
class AnimatedTextKit extends StatefulWidget {
/// Text animation.
final TypewriterAnimatedText animatedText;
const AnimatedTextKit({
Key key,
@required this.animatedText,
}) : super(key: key);
/// Creates the mutable state for this widget. See [StatefulWidget.createState].
@override
_AnimatedTextKitState createState() => _AnimatedTextKitState();
}
class _AnimatedTextKitState extends State<AnimatedTextKit>
with TickerProviderStateMixin {
AnimationController _controller;
@override
void initState() {
print(' ^^^ _AnimatedTextKitState:initState - ${this.hashCode}');
super.initState();
final animatedText = widget.animatedText;
_controller = AnimationController(
duration: animatedText.duration,
vsync: this,
);
animatedText.initAnimation(_controller);
_controller.repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
print(' ^^^ _AnimatedTextKitState:build - ${this.hashCode}');
return AnimatedBuilder(
animation: _controller,
builder: widget.animatedText.animatedBuilder,
);
}
}
Запустите его и нажмите на плавающую кнопку действия, чтобы увеличить счетчик, и результат будет выглядеть примерно так:
I/flutter (29275): ^^^ _AnimatedTextKitState:initState - 171683441
I/flutter (29275): >>> TypewriterAnimatedText:initAnimation - 936400533
I/flutter (29275): ^^^ _AnimatedTextKitState:build - 171683441
I/flutter (29275): >>> TypewriterAnimatedText:animatedBuilder - 936400533
I/chatty (29275): uid=10133(com.example.atk168) 1.ui identical 2 lines
I/flutter (29275): >>> TypewriterAnimatedText:animatedBuilder - 936400533
I/flutter (29275): >>> TypewriterAnimatedText:animatedBuilder - 936400533
I/chatty (29275): uid=10133(com.example.atk168) 1.ui identical 60 lines
I/flutter (29275): >>> TypewriterAnimatedText:animatedBuilder - 936400533
I/flutter (29275): *** _MyHomePageState:_incrementCounter - 220007591
I/flutter (29275): ^^^ _AnimatedTextKitState:build - 171683441
I/flutter (29275): >>> TypewriterAnimatedText:animatedBuilder - 280798896
I/flutter (29275): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (29275): The following NoSuchMethodError was thrown building AnimatedBuilder(animation:
I/flutter (29275): AnimationController#41b74(▶ 0.633), dirty, state: _AnimatedState#db314):
I/flutter (29275): The getter 'value' was called on null.
I/flutter (29275): Receiver: null
I/flutter (29275): Tried calling: value
I/flutter (29275):
I/flutter (29275): The relevant error-causing widget was:
I/flutter (29275): AnimatedBuilder file:///Users/anthony/github/awhitford/atk168/lib/main.dart:173:12
I/flutter (29275):
I/flutter (29275): When the exception was thrown, this was the stack:
I/flutter (29275): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
I/flutter (29275): #1 TypewriterAnimatedText.animatedBuilder (package:atk168/main.dart:121:45)
I/flutter (29275): #2 AnimatedBuilder.build (package:flutter/src/widgets/transitions.dart:1528:19)
I/flutter (29275): #3 _AnimatedState.build (package:flutter/src/widgets/transitions.dart:179:48)
I/flutter (29275): #4 StatefulElement.build (package:flutter/src/widgets/framework.dart:4825:27)
I/flutter (29275): #5 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4708:15)
I/flutter (29275): #6 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4880:11)
I/flutter (29275): #7 BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
I/flutter (29275): #8 Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
I/flutter (29275): #9 StatefulElement.update (package:flutter/src/widgets/framework.dart:4912:5)
I/flutter (29275): #10 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #11 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4733:16)
I/flutter (29275): #12 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4880:11)
I/flutter (29275): #13 BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
I/flutter (29275): #14 Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
I/flutter (29275): #15 StatefulElement.update (package:flutter/src/widgets/framework.dart:4912:5)
I/flutter (29275): #16 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #17 RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:5727:32)
I/flutter (29275): #18 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6327:17)
I/flutter (29275): #19 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #20 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6205:14)
I/flutter (29275): #21 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #22 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4733:16)
I/flutter (29275): #23 BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
I/flutter (29275): #24 Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
I/flutter (29275): #25 StatelessElement.update (package:flutter/src/widgets/framework.dart:4789:5)
I/flutter (29275): #26 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #27 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4733:16)
I/flutter (29275): #28 BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
I/flutter (29275): #29 Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
I/flutter (29275): #30 ProxyElement.update (package:flutter/src/widgets/framework.dart:5066:5)
I/flutter (29275): #31 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #32 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4733:16)
I/flutter (29275): #33 BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
I/flutter (29275): #34 Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
I/flutter (29275): #35 ProxyElement.update (package:flutter/src/widgets/framework.dart:5066:5)
I/flutter (29275): #36 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #37 RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:5727:32)
I/flutter (29275): #38 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6327:17)
I/flutter (29275): #39 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #40 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4733:16)
I/flutter (29275): #41 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4880:11)
I/flutter (29275): #42 BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
I/flutter (29275): #43 Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
I/flutter (29275): #44 StatefulElement.update (package:flutter/src/widgets/framework.dart:4912:5)
I/flutter (29275): #45 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #46 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4733:16)
I/flutter (29275): #47 BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
I/flutter (29275): #48 Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
I/flutter (29275): #49 ProxyElement.update (package:flutter/src/widgets/framework.dart:5066:5)
I/flutter (29275): #50 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #51 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4733:16)
I/flutter (29275): #52 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4880:11)
I/flutter (29275): #53 BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
I/flutter (29275): #54 Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
I/flutter (29275): #55 StatefulElement.update (package:flutter/src/widgets/framework.dart:4912:5)
I/flutter (29275): #56 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #57 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6205:14)
I/flutter (29275): #58 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #59 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4733:16)
I/flutter (29275): #60 BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
I/flutter (29275): #61 Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
I/flutter (29275): #62 StatelessElement.update (package:flutter/src/widgets/framework.dart:4789:5)
I/flutter (29275): #63 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #64 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6205:14)
I/flutter (29275): #65 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #66 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4733:16)
I/flutter (29275): #67 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4880:11)
I/flutter (29275): #68 BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
I/flutter (29275): #69 Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
I/flutter (29275): #70 StatefulElement.update (package:flutter/src/widgets/framework.dart:4912:5)
I/flutter (29275): #71 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #72 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4733:16)
I/flutter (29275): #73 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4880:11)
I/flutter (29275): #74 BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
I/flutter (29275): #75 Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
I/flutter (29275): #76 StatefulElement.update (package:flutter/src/widgets/framework.dart:4912:5)
I/flutter (29275): #77 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #78 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4733:16)
I/flutter (29275): #79 BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
I/flutter (29275): #80 Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
I/flutter (29275): #81 ProxyElement.update (package:flutter/src/widgets/framework.dart:5066:5)
I/flutter (29275): #82 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #83 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4733:16)
I/flutter (29275): #84 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4880:11)
I/flutter (29275): #85 BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
I/flutter (29275): #86 Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
I/flutter (29275): #87 StatefulElement.update (package:flutter/src/widgets/framework.dart:4912:5)
I/flutter (29275): #88 Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
I/flutter (29275): #89 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4733:16)
I/flutter (29275): #90 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4880:11)
I/flutter (29275): #91 BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
I/flutter (29275): #92 Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
I/flutter (29275): #93 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2813:33)
I/flutter (29275): #94 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:899:21)
I/flutter (29275): #95 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:320:5)
I/flutter (29275): #96 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1119:15)
I/flutter (29275): #97 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1057:9)
I/flutter (29275): #98 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:973:5)
I/flutter (29275): #102 _invoke (dart:ui/hooks.dart:157:10)
I/flutter (29275): #103 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:253:5)
I/flutter (29275): #104 _drawFrame (dart:ui/hooks.dart:120:31)
I/flutter (29275): (elided 3 frames from dart:async)
I/flutter (29275):
I/flutter (29275): ════════════════════════════════════════════════════════════════════════════════════════════════════
I/flutter (29275): Another exception was thrown: A RenderFlex overflowed by 99320 pixels on the bottom.
I/flutter (29275): >>> TypewriterAnimatedText:animatedBuilder - 280798896
I/flutter (29275): Another exception was thrown: NoSuchMethodError: The getter 'value' was called on null.
I/flutter (29275): >>> TypewriterAnimatedText:animatedBuilder - 280798896
Application finished.
В частности, внимательно посмотрите на это:
I/flutter (29275): >>> TypewriterAnimatedText:animatedBuilder - 936400533
I/flutter (29275): *** _MyHomePageState:_incrementCounter - 220007591
I/flutter (29275): ^^^ _AnimatedTextKitState:build - 171683441
I/flutter (29275): >>> TypewriterAnimatedText:animatedBuilder - 280798896
I/flutter (29275): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (29275): The following NoSuchMethodError was thrown building AnimatedBuilder(animation:
I/flutter (29275): AnimationController#41b74(▶ 0.633), dirty, state: _AnimatedState#db314):
I/flutter (29275): The getter 'value' was called on null.
I/flutter (29275): Receiver: null
I/flutter (29275): Tried calling: value
I/flutter (29275):
I/flutter (29275): The relevant error-causing widget was:
I/flutter (29275): AnimatedBuilder file:///Users/anthony/github/awhitford/atk168/lib/main.dart:173:12
I/flutter (29275):
I/flutter (29275): When the exception was thrown, this was the stack:
I/flutter (29275): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
I/flutter (29275): #1 TypewriterAnimatedText.animatedBuilder (package:atk168/main.dart:121:45)
I/flutter (29275): #2 AnimatedBuilder.build (package:flutter/src/widgets/transitions.dart:1528:19)
I/flutter (29275): #3 _AnimatedState.build (package:flutter/src/widgets/transitions.dart:179:48)
У меня есть только одно TypewriterAnimatedText
заявление. Он создает TypewriterAnimatedText
936400533
, который был правильно инициализирован, но после нажатия кнопки он начинает создавать новый экземпляр ( 280798896
), который не был инициализирован, и поэтому _typewriterText
имеет значение null, и вызов .value
этого вызывает эту проблему.
Что происходит? Я ожидаю, что build
это произойдет только после initState
… но, похоже, здесь это не так? (Обнаружил ли я Жука-трепетуна?)
Эту проблему было немного сложно воспроизвести. Я получил несколько жалоб от пользователей Windows, поэтому, похоже, они смогли легко воспроизвести это. Что касается меня, я смог воспроизвести это на своем Mac только после переключения на dev
канал и тестирования на эмуляторе Android.
Комментарии:
1. «но после нажатия кнопки он начинает создавать новый экземпляр (280798896), который не был инициализирован», — это потому, что вы вызываете
setState(() { _counter ; });
which перестраивает все дерево виджетов — это нормальное поведение2. Спасибо @pskink. Я смог разрешить этот конкретный пример, переместив
TypewriterAnimatedText
объявление за пределыbuild
метода, чтобы оно не создавалось заново . Я думал, что если он будет воссоздан заново, он будет повторно инициализирован, но я думаю, что это не относится к не-StatefulWidget.3. Я думаю, что я только что обнаружил, почему StatefulWidgets имеют два класса.