Flutter 使用 AnimatedIcon 代替 AppBar 前导图标
Flutter put AnimatedIcon instead of AppBar leading icon
在我的应用程序中,我尝试使用 AnimatedIcon
而不是 Appbar
、leading
图标来更改 AnimatedIcon
的控制器,我正在使用 Provider
包,但我得到这个错误:
setState() or markNeedsBuild() called during build.
我的AppBar
:
AppBar(
elevation: 8.0,
titleSpacing: 0.0,
automaticallyImplyLeading: true,
leading: Consumer<AppBarAnimatedIconMode>(
builder: (context, mode, child) => ValueListenableProvider.value(
value: mode.iconMode,
child: Consumer<bool>(
builder: (context, value, child) => AppBarAnimatedIcon(
animate: value,
)))),
AppBarAnimatedIcon
class:
class AppBarAnimatedIcon extends StatefulWidget {
final bool animate;
const AppBarAnimatedIcon({@required this.animate});
@override
State<StatefulWidget> createState() => _AppBarAnimatedIcon();
}
class _AppBarAnimatedIcon extends State<AppBarAnimatedIcon> with TickerProviderStateMixin {
AnimationController _animationController;
bool get _animate => widget.animate;
@override
void initState() {
super.initState();
_animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 500));
if(_animate){
_animationController.forward();
}else{
_animationController.reverse();
}
}
@override
Widget build(BuildContext context) {
return Center(
child: AnimatedIcon(
icon: AnimatedIcons.menu_close,
progress: _animationController,
),
);
}
}
ValueNotifier
class更改AnimatedIcon
动画控制器:
class AppBarAnimatedIconMode {
final ValueNotifier<bool> iconMode = ValueNotifier<bool>(false);
// ignore: use_setters_to_change_properties
void changeIconMode() {
iconMode.value = !iconMode.value;
}
}
完整错误信息:
This _DefaultInheritedProviderScope<bool> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: _DefaultInheritedProviderScope<bool>
value: false
The widget which was currently being built when the offending call was made was: BuildDefaultAppBar
dirty
dependencies: [_EffectiveTickerMode]
state: _BuildDefaultAppBar#a5ab5(tickers: tracking 1 ticker)
When the exception was thrown, this was the stack:
#0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:4167:11)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:4182:6)
#2 _InheritedProviderScopeMixin.markNeedsNotifyDependents (package:provider/src/inherited_provider.dart:269:5)
#3 _DeferredDelegateState.setState (package:provider/src/deferred_inherited_provider.dart:139:17)
#4 ValueListenableProvider._startListening.<anonymous closure>.<anonymous closure> (package:provider/src/value_listenable_provider.dart:74:38)
...
The ValueNotifier<bool> sending notification was: ValueNotifier<bool>#7aa18(true)
我创建了一个新项目并尝试了您的代码。首先,如果您使用 Provider,您应该将 ChangeNotifier 与您的控件 classes 一起使用。此外,您还应该将 ChangeNotifierProvider 与主 class 一起使用。这些是我的代码,运行良好。
main.dart
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: ChangeNotifierProvider(
create: (context) => AppBarAnimatedIconMode(), // This is condition class.
child: Home(),
),
);
}
}
AppBarAnimatedIconMode.dart
class AppBarAnimatedIconMode with ChangeNotifier {
final ValueNotifier<bool> iconMode = ValueNotifier<bool>(false);
// ignore: use_setters_to_change_properties
void changeIconMode() {
iconMode.value = !iconMode.value;
notifyListeners(); // For change the state of icon value.
}
}
AppBarAnimatedIcon.dart
class AppBarAnimatedIcon extends StatefulWidget {
final bool animate;
const AppBarAnimatedIcon({@required this.animate});
@override
State<StatefulWidget> createState() => _AppBarAnimatedIcon();
}
class _AppBarAnimatedIcon extends State<AppBarAnimatedIcon> with SingleTickerProviderStateMixin {// You should use SingleTickerProviderStateMixin //
AnimationController _animationController;
bool get _animate => widget.animate;
@override
void initState() {
super.initState();
_animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 500));
}
@override
void dispose() { // Also dispose your animation when you dispose the class.
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_animate) { // You should check your conditions in your build() for animate
_animationController.forward();
} else {
_animationController.reverse();
}
return Center(
child: AnimatedIcon(
icon: AnimatedIcons.menu_close,
progress: _animationController,
),
);
}
}
所以你可以像这个函数一样控制你的动画:
RaisedButton(
child: Text('Change'),
onPressed: () {
var provider = Provider.of<AppBarAnimatedIconMode>(context, listen: false);
provider.changeIconMode();
},
同样在您的 AppBar 上,您可以使用此代码。你的很复杂。
AppBar(
title: Text('Deneme'),
leading: Consumer<AppBarAnimatedIconMode>(
builder: (context, mode, child) => AppBarAnimatedIcon(
animate: mode.iconMode,
),
),
),
而 AppBarAnimatedIconMode.dart
class AppBarAnimatedIconMode with ChangeNotifier {
bool iconMode = false;
void changeIconMode() {
iconMode = !iconMode;
notifyListeners();
}
}
在我的应用程序中,我尝试使用 AnimatedIcon
而不是 Appbar
、leading
图标来更改 AnimatedIcon
的控制器,我正在使用 Provider
包,但我得到这个错误:
setState() or markNeedsBuild() called during build.
我的AppBar
:
AppBar(
elevation: 8.0,
titleSpacing: 0.0,
automaticallyImplyLeading: true,
leading: Consumer<AppBarAnimatedIconMode>(
builder: (context, mode, child) => ValueListenableProvider.value(
value: mode.iconMode,
child: Consumer<bool>(
builder: (context, value, child) => AppBarAnimatedIcon(
animate: value,
)))),
AppBarAnimatedIcon
class:
class AppBarAnimatedIcon extends StatefulWidget {
final bool animate;
const AppBarAnimatedIcon({@required this.animate});
@override
State<StatefulWidget> createState() => _AppBarAnimatedIcon();
}
class _AppBarAnimatedIcon extends State<AppBarAnimatedIcon> with TickerProviderStateMixin {
AnimationController _animationController;
bool get _animate => widget.animate;
@override
void initState() {
super.initState();
_animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 500));
if(_animate){
_animationController.forward();
}else{
_animationController.reverse();
}
}
@override
Widget build(BuildContext context) {
return Center(
child: AnimatedIcon(
icon: AnimatedIcons.menu_close,
progress: _animationController,
),
);
}
}
ValueNotifier
class更改AnimatedIcon
动画控制器:
class AppBarAnimatedIconMode {
final ValueNotifier<bool> iconMode = ValueNotifier<bool>(false);
// ignore: use_setters_to_change_properties
void changeIconMode() {
iconMode.value = !iconMode.value;
}
}
完整错误信息:
This _DefaultInheritedProviderScope<bool> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: _DefaultInheritedProviderScope<bool>
value: false
The widget which was currently being built when the offending call was made was: BuildDefaultAppBar
dirty
dependencies: [_EffectiveTickerMode]
state: _BuildDefaultAppBar#a5ab5(tickers: tracking 1 ticker)
When the exception was thrown, this was the stack:
#0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:4167:11)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:4182:6)
#2 _InheritedProviderScopeMixin.markNeedsNotifyDependents (package:provider/src/inherited_provider.dart:269:5)
#3 _DeferredDelegateState.setState (package:provider/src/deferred_inherited_provider.dart:139:17)
#4 ValueListenableProvider._startListening.<anonymous closure>.<anonymous closure> (package:provider/src/value_listenable_provider.dart:74:38)
...
The ValueNotifier<bool> sending notification was: ValueNotifier<bool>#7aa18(true)
我创建了一个新项目并尝试了您的代码。首先,如果您使用 Provider,您应该将 ChangeNotifier 与您的控件 classes 一起使用。此外,您还应该将 ChangeNotifierProvider 与主 class 一起使用。这些是我的代码,运行良好。
main.dart
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: ChangeNotifierProvider(
create: (context) => AppBarAnimatedIconMode(), // This is condition class.
child: Home(),
),
);
}
}
AppBarAnimatedIconMode.dart
class AppBarAnimatedIconMode with ChangeNotifier {
final ValueNotifier<bool> iconMode = ValueNotifier<bool>(false);
// ignore: use_setters_to_change_properties
void changeIconMode() {
iconMode.value = !iconMode.value;
notifyListeners(); // For change the state of icon value.
}
}
AppBarAnimatedIcon.dart
class AppBarAnimatedIcon extends StatefulWidget {
final bool animate;
const AppBarAnimatedIcon({@required this.animate});
@override
State<StatefulWidget> createState() => _AppBarAnimatedIcon();
}
class _AppBarAnimatedIcon extends State<AppBarAnimatedIcon> with SingleTickerProviderStateMixin {// You should use SingleTickerProviderStateMixin //
AnimationController _animationController;
bool get _animate => widget.animate;
@override
void initState() {
super.initState();
_animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 500));
}
@override
void dispose() { // Also dispose your animation when you dispose the class.
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_animate) { // You should check your conditions in your build() for animate
_animationController.forward();
} else {
_animationController.reverse();
}
return Center(
child: AnimatedIcon(
icon: AnimatedIcons.menu_close,
progress: _animationController,
),
);
}
}
所以你可以像这个函数一样控制你的动画:
RaisedButton(
child: Text('Change'),
onPressed: () {
var provider = Provider.of<AppBarAnimatedIconMode>(context, listen: false);
provider.changeIconMode();
},
同样在您的 AppBar 上,您可以使用此代码。你的很复杂。
AppBar(
title: Text('Deneme'),
leading: Consumer<AppBarAnimatedIconMode>(
builder: (context, mode, child) => AppBarAnimatedIcon(
animate: mode.iconMode,
),
),
),
而 AppBarAnimatedIconMode.dart
class AppBarAnimatedIconMode with ChangeNotifier {
bool iconMode = false;
void changeIconMode() {
iconMode = !iconMode;
notifyListeners();
}
}