В настоящее время я читаю «Поваренную книгу Flutter — второе издание» Симоны Алессандрии и обнаружил исключение в главе 6, связанное с файлом stopwatch.dart. Когда я запускаю код, я получаю следующее исключение:
The AnimationController notifying status listeners was:
AnimationController#a6c5f(▶ 0.000)
════════════════════════════════════════════════════════════════════════════════════════════════════
8
Another exception was thrown: The Scrollbar's ScrollController has no ScrollPosition attached.
Вот файл stopwatch.dart:
import 'package:flutter/material.dart';
import 'dart:async';
class StopWatch extends StatefulWidget {
const StopWatch({super.key});
@override
State<StopWatch> createState() => _StopWatchState();
}
class _StopWatchState extends State<StopWatch> {
bool isTicking = false;
int milliseconds = 0;
late Timer timer;
final laps = <int>[];
final itemHeight = 60.0;
final scrollController = ScrollController();
void _onTick(Timer time) {
if (mounted) {
setState(() {
milliseconds += 100;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Stopwatch'),
),
body: Column(
children: [
Expanded(child: _buildCounter(context)),
Expanded(child: _buildLapDisplay()),
],
),
);
}
Widget _buildCounter(BuildContext context) {
return Container(
color: Theme.of(context).primaryColor,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Lap ${laps.length + 1}',
style: Theme.of(context)
.textTheme
.headlineSmall!
.copyWith(color: Colors.white),
),
Text(
_secondsText(milliseconds),
style: Theme.of(context)
.textTheme
.headlineSmall!
.copyWith(color: Colors.white),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(Colors.green),
foregroundColor: WidgetStateProperty.all<Color>(Colors.white),
),
onPressed: isTicking ? null : _startTimer,
child: const Text('Start'),
),
const SizedBox(width: 20),
const SizedBox(width: 20),
ElevatedButton(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Colors.yellow),
),
onPressed: isTicking ? _lap : null,
child: const Text('Lap'),
),
SizedBox(width: 20),
TextButton(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(Colors.red),
foregroundColor: WidgetStateProperty.all<Color>(Colors.white),
),
onPressed: isTicking ? _stopTimer : null,
child: const Text('Stop'),
),
],
)
],
),
);
}
Widget _buildLapDisplay() {
return Scrollbar(
child: ListView.builder(
controller: scrollController,
itemExtent: itemHeight,
itemCount: laps.length,
itemBuilder: (context, index) {
final milliseconds = laps[index];
return ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 50),
title: Text('Lap ${index + 1}'),
trailing: Text(_secondsText(milliseconds)),
);
},
),
);
}
@override
void dispose() {
timer.cancel();
scrollController.dispose();
super.dispose();
}
void _startTimer() {
timer = Timer.periodic(const Duration(milliseconds: 1), _onTick);
setState(() {
milliseconds = 0;
laps.clear();
isTicking = true;
});
}
String _secondsText(int milliseconds) {
final seconds = milliseconds / 1000;
return '$seconds seconds';
}
void _stopTimer() {
timer.cancel();
setState(() {
isTicking = false;
});
}
void _lap() {
setState(() {
laps.add(milliseconds);
milliseconds = 0;
});
scrollController.animateTo(
itemHeight * laps.length,
duration: const Duration(milliseconds: 500),
curve: Curves.easeIn,
);
}
}
Я следовал коду из книги, но не уверен, что вызывает исключение. Кажется, проблема заключается в ScrollController полосы прокрутки, но я не могу точно определить проблему. Может ли кто-нибудь помочь мне понять, почему возникает это исключение и как его исправить?
Полный код можно найти на сайте автора GitHub.





Следующее утверждение было выдано при уведомлении прослушивателей статуса для AnimationController: ScrollController полосы прокрутки не имеет ScrollPosition прилагается. Полоса прокрутки не может быть нарисована без ПрокруткаПозиция. Полоса прокрутки попыталась использовать Первичныйконтроллер прокрутки. Этот ScrollController должен быть связан с помощью ScrollView, к которому применяется полоса прокрутки. Если ScrollController не предоставлен, PrimaryScrollController используется по умолчанию на мобильных платформах для ScrollViews с Ось. Вертикальное направление прокрутки. Чтобы использовать PrimaryScrollController явно установите для ScrollView.primary значение true в виджете с возможностью прокрутки.
Scrollbar над ListView.builder.Конструктор уже визуализирует полосу прокрутки, поэтому было бы излишним ее наличие. Должно выглядеть так:
Widget _buildLapDisplay() {
return ListView.builder(
...
scrollController в виджет Scrollbar.Если вам это нужно для украшения или другой функциональности, вот так:
Widget _buildLapDisplay() {
return Scrollbar(
controller: scrollController,
child: ListView.builder(
controller: scrollController,
...
Ваше первое решение сработало. Спасибо.