Closed glaceon2000 closed 2 years ago
I don't have any issues with your code above, is something I missed?
@mchome Did you try opening, then close then open the Dialog again? The exception is caught when I open the dialog again. It doesn't throw this exception once I remove the hexInputController value so I reckon it's something internal with the ColorPicker widget.
The following assertion was thrown while dispatching notifications for TextEditingController:
setState() called after dispose(): _ColorPickerState#a6d2e(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
Please provide the full code & steps that I can reproduce the bug.
@mchome Steps:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(title: const Text(_title)),
body: MyStatefulWidget(),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
height: 196,
child: ColorPickerTextField(
color: Colors.red,
),
),
),
);
}
}
class ColorPickerTextField extends StatefulWidget {
const ColorPickerTextField(
{Key? key, required this.color, this.onColorChanged})
: super(key: key);
final Color color;
final void Function(String)? onColorChanged;
@override
_ColorPickerTextFieldState createState() => _ColorPickerTextFieldState();
}
class _ColorPickerTextFieldState extends State<ColorPickerTextField> {
late Color _color;
final _controller = TextEditingController();
@override
void initState() {
super.initState();
_color = widget.color;
_controller.text = _color.value.toRadixString(16).toUpperCase();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return TextFormField(
controller: _controller,
onTap: () => showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text(
'Pick a color!',
),
content: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
ColorPicker(
displayThumbColor: true,
hexInputController: _controller,
pickerColor: _color,
onColorChanged: (color) {
setState(() {
_color = color;
});
},
showLabel: true,
pickerAreaHeightPercent: 0.8,
),
Container(
width: 300,
padding: const EdgeInsets.all(16),
child: TextFormField(
controller: _controller,
autofocus: true,
readOnly: true,
maxLength: 8,
inputFormatters: [
UpperCaseTextFormatter(),
FilteringTextInputFormatter.allow(
RegExp(kValidHexPattern),
),
],
),
),
],
),
),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('Done'),
),
],
),
));
}
}
class UpperCaseTextFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(oldValue, TextEditingValue newValue) =>
TextEditingValue(
text: newValue.text.toUpperCase(), selection: newValue.selection);
}
Nan, here's my log:
Restarted application in 1,158ms.
D/InputMethodManager( 9702): showSoftInput() view=io.flutter.embedding.android.FlutterView{9712b15 VFED..... .F...... 0,0-1080,2028 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
W/IInputConnectionWrapper( 9702): beginBatchEdit on inactive InputConnection
W/IInputConnectionWrapper( 9702): endBatchEdit on inactive InputConnection
W/IInputConnectionWrapper( 9702): beginBatchEdit on inactive InputConnection
W/IInputConnectionWrapper( 9702): endBatchEdit on inactive InputConnection
D/InsetsController( 9702): show(ime(), fromIme=true)
D/InsetsController( 9702): show(ime(), fromIme=true)
D/InputMethodManager( 9702): showSoftInput() view=io.flutter.embedding.android.FlutterView{9712b15 VFED..... .F...... 0,0-1080,2028 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
W/IInputConnectionWrapper( 9702): beginBatchEdit on inactive InputConnection
W/IInputConnectionWrapper( 9702): getTextBeforeCursor on inactive InputConnection
W/IInputConnectionWrapper( 9702): getTextAfterCursor on inactive InputConnection
W/IInputConnectionWrapper( 9702): getSelectedText on inactive InputConnection
W/IInputConnectionWrapper( 9702): endBatchEdit on inactive InputConnection
W/IInputConnectionWrapper( 9702): beginBatchEdit on inactive InputConnection
W/IInputConnectionWrapper( 9702): getTextAfterCursor on inactive InputConnection
W/IInputConnectionWrapper( 9702): endBatchEdit on inactive InputConnection
W/IInputConnectionWrapper( 9702): beginBatchEdit on inactive InputConnection
W/IInputConnectionWrapper( 9702): getTextAfterCursor on inactive InputConnection
W/IInputConnectionWrapper( 9702): endBatchEdit on inactive InputConnection
D/InsetsController( 9702): show(ime(), fromIme=true)
D/InsetsController( 9702): show(ime(), fromIme=true)
D/InputMethodManager( 9702): showSoftInput() view=io.flutter.embedding.android.FlutterView{9712b15 VFED..... .F...... 0,0-1080,2028 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/InsetsController( 9702): show(ime(), fromIme=true)
D/InputMethodManager( 9702): showSoftInput() view=io.flutter.embedding.android.FlutterView{9712b15 VFED..... .F...... 0,0-1080,2028 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/InsetsController( 9702): show(ime(), fromIme=true)
Device: Pixel 3 (Android 12) Flutter: 2.6.0-11.0.pre
I'm currently using it on Flutter Web - Chrome. I didn't expect this to be a platform issue
Oh, maybe I am writing v1.0.0, I will try v0.6.0 again.
I actually just tested on Pixel 4 XL (Android 11) - Flutter 2.5.2 stable and it produced the same exception. Would you kindly check again
I post v0.6.1 with the fixes.
May I ask what led to the exception and how you handled it?
Just remove the listener of textController in dispose()
of ColorPicker
.
colorpicker.dart:
class ColorPicker extends StatefulWidget {
...
@override
void dispose() {
widget.hexInputController?.removeListener(colorPickerTextInputListener);
super.dispose();
}
...
The code that throws this exception. This exception is thrown when I close the Dialog and open it again, and is not thrown once I remove the controller.