Пытаясь использовать image_picker
во флаттере, у меня возникает следующая проблема:
Когда навигация должна вернуться к Widget Nr1, я больше не могу звонить setState()
внутриВиджет Nr1.Это связано с тем, что метод dispose()
был вызван после того, как произошло Navigation.push
от Widget-Nr1 до Widget-Nr2.
Оказывается, мне абсолютно необходимо вызвать этот метод dispose()
вДля корректной работы плагина image_picker
.(если я этого не сделаю, то ошибка ...was disposed with an active Ticker...
произойдет, возможно, из-за того, что плагин image_picker
делает что-то под капотом, что заранее отчаянно нуждается в dispose ().
В любом случае, я чувствую, чтозмея кусает свой хвост.
В качестве резюме я делаю следующее (см. также код ниже):
- внутри виджета Nr1: нажатие кнопки FloatingAction переводит навигатор в виджетNr2
- оба виджета (Nr1 и Nr2) являются виджетами с отслеживанием состояния
- они оба имеют метод dispose (необходим, иначе image_picker не работает)
- Widget-Nr2 вызывает
image_picker
плагин (позволяющий пользователю сделать снимок с помощью камеры и запрашивающий у него некоторый текст String, описывающий изображение) - результат (то есть imageFile и некоторый текст String) должен быть возвращен Widget-Nr1 (используя
Navigation.pop
) - Widget-Nr1 действительно получает эти данные (т. Е. Изображение плюс некоторый текст строки)
- , но: он больше не может вызывать
setState()
после навигации.pop скорее всего из-затот факт, что оба виджета уже вызвали свой dispose()
метод
Я получаю сообщение об ошибке внутри Widget-Nr1:
Dart Error: Unhandled exception:
setState() called after dispose()
Что я могу сделать, чтобы эта работа работала?
Как я могу использовать данные результата image_picker
(для которого требуется dispose()
в Widget-1) в качестве результата Navigation.pop снова в Widget-1, и это таким образом, что setState()
все-таки возможно все-таки навигация ??
Или есть другой подход?
Вот мой код:
StatefulWidget Nr1 (отрывок из него):
child: FloatingActionButton(
onPressed: () async {
_imagePickerResult = await navigateToImagePicker(context);
setState(() async {
this.itemBins.add(ItemBin(
_imagePickerResult.locationName,
_imagePickerResult.locationImage));
});
},
child: Icon(Icons.add),
),
// ...
Future<ImagePickerResult> navigateToImagePicker(BuildContext context) async {
return await Navigator.push(
context, MaterialPageRoute(builder: (context) => MyImagePickerView())
);
}
// ...
class ImagePickerResult {
String locationName;
Image locationImage;
ImagePickerResult({this.locationName, this.locationImage});
}
StatefulWidget Nr2:
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:image_picker/image_picker.dart';
import './../../models/image_picker_location.dart';
class MyImagePickerView extends StatefulWidget {
_MyImagePickerViewState createState() => _MyImagePickerViewState();
}
class _MyImagePickerViewState extends State<MyImagePickerView> {
TextEditingController _myController = TextEditingController();
File _imageFile;
bool _pickImage = true;
@override
Widget build(BuildContext context) {
if (_pickImage) {
return FutureBuilder<File>(
future: ImagePicker.pickImage(source: ImageSource.camera),
builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
if (snapshot.hasData) {
_pickImage = false;
_imageFile = snapshot.data;
return _showImage(snapshot.data);
} else {
return Scaffold(
body: Center(
child: Text('no image picker availalbe'),
),
);
}
},
);
} else {
return _showImage(_imageFile);
}
}
Widget _showImage(File imgFile) {
return Scaffold(
body: Stack(
alignment: AlignmentDirectional.topStart,
children: <Widget>[
Positioned(
left: 0.0,
bottom: 0.0,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Center(
child: imgFile == null
? Text('No image selected.')
: Image.file(imgFile),
),
),
Positioned(
left: 16.0,
bottom: 70.0,
width: MediaQuery.of(context).size.width - 32.0,
height: 50.0,
child: Container(
color: Colors.grey[100],
child: TextField(
autofocus: false,
keyboardType: TextInputType.text,
autocorrect: false,
style: TextStyle(
color: Colors.black,
fontSize: 22.0,
fontWeight: FontWeight.w600),
decoration: InputDecoration(
hintStyle: TextStyle(
color: Colors.black38,
fontSize: 22.0,
fontWeight: FontWeight.normal),
hintText: "depart From :",
contentPadding: const EdgeInsets.fromLTRB(6.0, 13.0, 0, 12.0),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.red, width: 2.0),
),
),
maxLines: 1,
textAlign: TextAlign.left,
controller: _myController,
onEditingComplete: () {
FocusScope.of(context)
.requestFocus(FocusNode()); // dismiss keyboard
Navigator.pop(
context,
ImagePickerResult(
locationName: _myController.text,
locationImage: Image.file(imgFile),
),
);
},
),
),
),
],
),
);
}
}
метод dispose-Widget Nr1:
@override
void dispose() {
if (_debounce?.isActive ?? false) {
_debounce.cancel(); // if _debounce is active cancel it...
}
_debounce = Timer(const Duration(milliseconds: 200), () {
// security wait due to the fact that there are animations still running during setState()
});
// dispose AnimationController
controller.dispose();
_debounce.cancel();
super.dispose();
}
метод dispose Widget-Nr2:
@override
void dispose() {
_myController.dispose();
super.dispose();
}
Вот сообщение об ошибке, если я не заставляю view1 выполнить dispose () до запуска image_picker ... (обратите внимание, что в момент, когда пользователь хочет запустить image_picker, запускается анимация и, следовательно, dispose ()делает искусственное «ожидание» 200 мс, прежде чем происходит переход к image_picker) ....
flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
flutter: The following assertion was thrown while finalizing the widget tree:
flutter: _HistoryViewState#a8eac(ticker active but muted) was disposed with an active Ticker.
flutter: _HistoryViewState created a Ticker via its SingleTickerProviderStateMixin, but at the time dispose()
flutter: was called on the mixin, that Ticker was still active. The Ticker must be disposed before calling
flutter: super.dispose(). Tickers used by AnimationControllers should be disposed by calling dispose() on the
flutter: AnimationController itself. Otherwise, the ticker will leak.
flutter: The offending ticker was: Ticker(created by _HistoryViewState#a8eac(lifecycle state: created))
flutter: The stack trace when the Ticker was actually created was:
flutter: #0 new Ticker.<anonymous closure>
package:flutter/…/scheduler/ticker.dart:64
flutter: #1 new Ticker
package:flutter/…/scheduler/ticker.dart:66
flutter: #2 __HistoryViewState&State&SingleTickerProviderStateMixin.createTicker
package:flutter/…/widgets/ticker_provider.dart:93
flutter: #3 new AnimationController