Flutter Hooks 与 flutter_map_animations 冲突:第二次地图动画报错的解决方案
在 HookWidget 中使用 flutter_map_animations 时,如果把 useSingleTickerProvider() 传给 AnimatedMapController:
dart
final tickerProvider = useSingleTickerProvider();
final animatedMapController = useMemoized(
() => AnimatedMapController(
vsync: tickerProvider,
),
);
第一次地图动画可以正常执行。
第二次执行动画时,会抛出:
css
A SingleTickerProviderStateMixin can only be used as a TickerProvider once.
If a State is used for multiple AnimationController objects, or if it is
passed to other objects and those objects might use it more than one time
in total, then instead of mixing in a SingleTickerProviderStateMixin,
use a regular TickerProviderStateMixin.
原因是 useSingleTickerProvider() 只能创建一个 Ticker,而 AnimatedMapController 在多次地图动画中可能创建多个 AnimationController。
flutter_hooks 已经有一个增加 useMultiTickerProvider() 的 PR:
但该功能还没有进入正式版本,因此可以先在项目中加入一个自定义实现:
dart
import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
TickerProvider useMultiTickerProvider({List<Object?>? keys}) {
return use(
keys != null
? _MultiTickerProviderHook(keys)
: const _MultiTickerProviderHook(),
);
}
class _MultiTickerProviderHook extends Hook<TickerProvider> {
const _MultiTickerProviderHook([List<Object?>? keys])
: super(keys: keys);
@override
_MultiTickerProviderHookState createState() =>
_MultiTickerProviderHookState();
}
class _MultiTickerProviderHookState
extends HookState<TickerProvider, _MultiTickerProviderHook>
implements TickerProvider {
final Set<Ticker> _tickers = <Ticker>{};
ValueListenable<bool>? _tickerModeNotifier;
@override
Ticker createTicker(TickerCallback onTick) {
final ticker = Ticker(
onTick,
debugLabel: 'created by $context (multi)',
);
_updateTickerModeNotifier();
_tickers.add(ticker);
_updateTickers();
return ticker;
}
@override
void dispose() {
assert(() {
for (final ticker in _tickers) {
if (ticker.isActive) {
throw FlutterError(
'useMultiTickerProvider created Ticker(s), but at the time '
'dispose() was called on the Hook, at least one of those Tickers '
'was still active.',
);
}
}
return true;
}(), '');
_tickerModeNotifier?.removeListener(_updateTickers);
_tickerModeNotifier = null;
_tickers.clear();
super.dispose();
}
@override
TickerProvider build(BuildContext context) {
_updateTickerModeNotifier();
_updateTickers();
return this;
}
void _updateTickers() {
if (_tickers.isEmpty) {
return;
}
final muted =
!(_tickerModeNotifier?.value ?? TickerMode.of(context));
for (final ticker in _tickers) {
ticker.muted = muted;
}
}
void _updateTickerModeNotifier() {
final newNotifier = TickerMode.getNotifier(context);
if (newNotifier == _tickerModeNotifier) {
return;
}
_tickerModeNotifier?.removeListener(_updateTickers);
newNotifier.addListener(_updateTickers);
_tickerModeNotifier = newNotifier;
}
@override
String get debugLabel => 'useMultiTickerProvider';
@override
bool get debugSkipValue => true;
}
然后把:
dart
final tickerProvider = useSingleTickerProvider();
改成:
dart
final tickerProvider = useMultiTickerProvider();
final animatedMapController = useMemoized(
() => AnimatedMapController(
vsync: tickerProvider,
),
);
将 useSingleTickerProvider() 替换为 useMultiTickerProvider(),避免 AnimatedMapController 第二次创建动画时触发单 Ticker 限制。