A TextEditingController was used after being disposed in Flutter - flutter

I made two TextEditing Controllers and initialized them. Also, I initialized them in my initState too, so that when my screen is disposed they can be initialized again, then why am I getting this error? The two text controllers are used in various places and the OTPBackButton is the widget which takes us back to the previous screen.
When I am opening this screen for first time, it works fine but when I click on this OTPBackButton (or simply the back button) and come back to this screen again, this error is shown to me.
I am including full widget if it helps -
class MobileInput extends StatefulWidget {
final bool isOTP;
final bool isSignup;
VoidCallback signUpState;
TwoArgumentEventCallback signInWithOTP;
Callback sendOTP;
MobileInput({
this.isOTP = true,
required this.sendOTP,
required this.signInWithOTP,
required this.signUpState,
this.isSignup = false,
});
#override
_MobileInputState createState() => _MobileInputState();
}
class _MobileInputState extends State<MobileInput> {
.....
TextEditingController contactController = TextEditingController();
TextEditingController otpController = TextEditingController();
.....
#override
void initState() {
...
contactController = TextEditingController();
otpController = TextEditingController();
super.initState();
}
#override
void dispose() {
// contactController.dispose();
// otpController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
.........
(isOTP)
? Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: text.bodyText2!
.apply(color: secondaryTextColor),
children: [
TextSpan(
text:
'We sent a verification code to your\nphone number '),
TextSpan(
text: (contactController.text.isNotEmpty)
? '(+91) ' +
contactController.text.toString()
: '',
style:
text.bodyText2!.apply(color: brandPurple),
),
],
),
),
)
: Container(),
SizedBox(
height: getProportionateScreenWidth(32),
),
isOTP
? PinCodeTextField(
animationType: AnimationType.scale,
focusNode: focusNode,
onChanged: (value) {
print(otpController.text + ' after input');
if (otpController.text.length < 6) {
setState(() {
isDisabled = true;
});
}
},
.....
),
onCompleted: (value) {
otpController.text = value;
setState(() {
otpButtonText = "Verify Phone Number";
isDisabled = false;
});
},
)
: CustomTextField(
.....
onChanged: (value) {
if (isSignup) {
if (contactController.text.length == 10) {
FocusScope.of(context).unfocus();
emailEntered = true;
if (checkboxValue) {
setState(() {
isDisabled = false;
});
} else {
setState(() {
isDisabled = true;
});
}
} else {
setState(() {
isDisabled = true;
});
}
} else {
if (contactController.text.length == 10) {
FocusScope.of(context).unfocus();
emailEntered = true;
setState(() {
isDisabled = false;
});
} else {
setState(() {
isDisabled = true;
});
}
}
},
......
),
.....
......
isOTP
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Didn\'t received?',
style: text.bodyText2!.apply(color: Colors.white),
),
TextButton(
onPressed: () {
setState(() {
otpController.clear();
focusNode.requestFocus();
// autoFocus = true;
});
(otpController.text.isEmpty)
? widget.sendOTP(contactController.text)
: null;
},
child: RichText(
........
)
: Container(),
SizedBox(height: getProportionateScreenWidth(20)),
isOTP
? LoginCTA(
//After input otp
onPressed: () async {
.....
if (emailEntered &&
otpController.text.length == 6) {
bool res;
try {
res = await widget.signInWithOTP(
contactController.text, otpController.text);
} catch (e) {
res = false;
print(e);
}
..............
LoginCTA(
//After input mobile number
onPressed: () async {
if (emailEntered &&
contactController.text.length == 10) {
widget.sendOTP(contactController.text);
setState(() {
isOTP = true;
isDisabled = true;
});
} else {
......
}
},
.......
),
SizedBox(height: getProportionateScreenWidth(28)),
!isOTP
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
(isSignup)
? 'Already have an account? '
: 'Don\'t have an account? ',
style: TextStyle(
fontSize: 16,
color: Colors.white,
),
),
GestureDetector(
onTap: () => setState(() {
isSignup = !isSignup;
if (isSignup) {
if (contactController.text.length == 10) {
FocusScope.of(context).unfocus();
emailEntered = true;
if (checkboxValue) {
setState(() {
isDisabled = false;
});
} else {
setState(() {
isDisabled = true;
});
}
} else {
setState(() {
isDisabled = true;
});
}
} else {
if (contactController.text.length == 10) {
FocusScope.of(context).unfocus();
emailEntered = true;
setState(() {
isDisabled = false;
});
} else {
setState(() {
isDisabled = true;
});
}
}
}),
.......
],
)
: Container(),
],
),
),
],
),
),
);
}
}

You need to mark your TextEditingController instances as late final when you define them and then initialize them in the initState() function and dispose of them in dispose() as I show you here:
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late final TextEditingController _textController;
#override
void initState() {
_textController = TextEditingController();
super.initState();
}
#override
void dispose() {
_textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
throw UnimplementedError();
}
}

Related

how to Enable button if "All textfield" is not empty flutter

my app has two textformfield. I want to enable button if all textfield are not empty.
In the internet, there are a way to enable button if "one" textfield are not empty.
I try to make isFilledTitle true if title of textfield is not empty. and isFilledContent true if content of textfield is not empty. and then if they are all true, isButtonActive is true. but it doesn't work.
late TextEditingController _titleEditingController;
late TextEditingController _contentEditingController;
bool isButtonActive = true;
bool isFilledContent = false;
bool isFilledTitle = false;
#override
void initState() {
super.initState();
_titleEditingController = TextEditingController();
_contentEditingController = TextEditingController();
_titleEditingController.addListener(() {
final isFilledTitle = _titleEditingController.text.isNotEmpty;
setState(() {
this.isFilledTitle = isFilledTitle;
});
});
_contentEditingController.addListener(() {
final isFilledContent = _contentEditingController.text.isNotEmpty;
setState(() {
this.isFilledContent = isFilledContent;
});
});
if(isFilledContent && isFilledTitle){
setState(() {
isButtonActive = true;
});
} else {
setState(() {
isButtonActive = false;
});
}
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: Container(
child: SafeArea(
child: Scaffold(
appBar: AppBarWriteContainer(
onButtonPressed: isButtonActive
? (widget.postNo != null)
? () => revisePost()
: () => newPost()
: null,
),
Yeah your answer is correct but the user needs validation on two text field so i will modify the answer like
class _TempDialogState extends State<TempDialog> {
final TextEditingController _inputController = TextEditingController();
final TextEditingController _inputController2 = TextEditingController();
bool enable = false;
#override
void initState() {
super.initState();
}
#override
void dispose() {
_inputController.dispose();
_inputController2.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Center(
child: Column(children: [
TextField(
controller: _inputController,
onChanged: (data) {
if (_inputController.text.isEmpty ||
_inputController2.text.isEmpty) {
enable = false;
} else {
enable = true;
}
setState(() {});
},
),
TextField(
controller: _inputController2,
onChanged: (data) {
if (_inputController.text.isEmpty ||
_inputController2.text.isEmpty) {
enable = false;
} else {
enable = true;
}
setState(() {});
},
),
ElevatedButton(
onPressed: enable ? () {} : null,
child: Text('${enable}'),
)
])),
),
);
}
}
You don't need any additional libraries to do that. Flutter has it out-of-the-box and you can make sure you're not going to rebuild the whole tree on each keystroke.
TextEditingController extends ValueNotifier<TextEditingValue> which means you can utilize ValueListenableBuilder from material package to listen to text changes.
class MyWidget extends StatelessWidget {
final TextEditingController _inputController = TextEditingController();
#override
void dispose() {
_inputController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Column(children: [
TextField(controller: _inputController),
ValueListenableBuilder<TextEditingValue>(
valueListenable: _inputController,
builder: (context, value, child) {
return ElevatedButton(
onPressed: value.text.isNotEmpty ? () {} : null,
child: Text('I am disabled when text is empty'),
);
},
),
]);
}
}
You only set the isButtonActive in the initState. Try updating it in the controller listeners. Maybe something like:
#override
void initState() {
super.initState();
_titleEditingController = TextEditingController();
_contentEditingController = TextEditingController();
_titleEditingController.addListener(() {
final isFilledTitle = _titleEditingController.text.isNotEmpty;
setState(() {
this.isFilledTitle = isFilledTitle;
updateIsButtonActive();
});
});
_contentEditingController.addListener(() {
final isFilledContent = _contentEditingController.text.isNotEmpty;
setState(() {
this.isFilledContent = isFilledContent;
updateIsButtonActive();
});
});
}
void updateIsButtonActive() {
if(isFilledContent && isFilledTitle){
isButtonActive = true;
} else {
isButtonActive = false;
}
}

Connection lost - OnTap Text to play file => getting connection lost

I am trying to get the user to press a record button, to then speak. His memo is recorded and then when the user tap on the Text widget displaying the name of the audio file, the file should be read so he can listen again to what he has recorded.
When I try, I am getting connection lost and the simulator crash. I do not find what is the problem in my code. I guess it is because I am still new with Flutter. Your help is welcome as I am stuck with this for several days. Many thanks.
import 'dart:io';
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:flutter_audio_recorder2/flutter_audio_recorder2.dart';
import 'package:path_provider/path_provider.dart';
enum RecordingState {
UnSet,
Set,
Recording,
Stopped,
}
void main66() => runApp(MyTest());
bool isRecording = false;
class MyTest extends StatelessWidget {
const MyTest({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
// backgroundColor: Colors.white,
body: HomeViewRecorder(),//HomeViewRecorder(),
),
);
}
}
class HomeViewRecorder extends StatefulWidget {
final String _title;
const HomeViewRecorder({Key key, String title}) : _title = title,
super(key: key);
#override
_HomeViewRecorderState createState() => _HomeViewRecorderState();
}
class _HomeViewRecorderState extends State<HomeViewRecorder> {
Directory appDirectory;
String records = '';
#override
void initState() {
super.initState();
getApplicationDocumentsDirectory().then((value) {
appDirectory = (value);
setState(() {
print(appDirectory);
});
});
}
void dispose() {
appDirectory.delete();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold (
appBar: AppBar(
title: Text('My recorder Test')),
body: Column(
children: [
Expanded(child:
InkWell(child: RecordListView(records:records)),
),
Center(child: Record_Widget(
onSaved:
_onRecordComplete)),
]
)
);
}
_onRecordComplete() {
records ='';
appDirectory.list().listen((onData) {
if (onData.path.contains('.aac')) records=(onData.path);
}).onDone(() {
setState(() {});
});
}
}
class RecordListView extends StatefulWidget {
final String records;
const RecordListView({
Key key,
this.records,
}) : super(key: key);
#override
_RecordListViewState createState() => _RecordListViewState();
}
class _RecordListViewState extends State<RecordListView> {
int _totalDuration;
int _currentDuration;
double _completedPercentage = 0.0;
bool _isPlaying = false;
int _selectedIndex = -1;
#override
Widget build(BuildContext context){
return Column(
children: [
widget.records.isEmpty?
Text('No records yet'): InkWell(child: Text(widget.records.split("/").last+DateTime.now().toString()),
onTap: _onPlay,),
],
);
}
Future<void> _onPlay({ String filePath, int index}) async {
AudioPlayer audioPlayer = AudioPlayer();
if (!_isPlaying) {
audioPlayer.play(filePath, isLocal: true);
setState(() {
_selectedIndex = index;
_completedPercentage = 0.0;
_isPlaying = true;
});
audioPlayer.onPlayerCompletion.listen((_) {
setState(() {
_isPlaying = false;
_completedPercentage = 0.0;
});
});
audioPlayer.onDurationChanged.listen((duration) {
setState(() {
_totalDuration = duration.inMicroseconds;
});
});
audioPlayer.onAudioPositionChanged.listen((duration) {
setState(() {
_currentDuration = duration.inMicroseconds;
_completedPercentage =
_currentDuration.toDouble() / _totalDuration.toDouble();
});
});
}
}
String _getDateFromFilePath({ String filePath}) {
String fromEpoch = filePath.substring(
filePath.lastIndexOf('/') + 1, filePath.lastIndexOf('.'));
DateTime recordedDate =
DateTime.fromMillisecondsSinceEpoch(int.parse(fromEpoch));
int year = recordedDate.year;
int month = recordedDate.month;
int day = recordedDate.day;
return ('$year-$month-$day');
}
}
class Record_Widget extends StatefulWidget {
final Function onSaved;
const Record_Widget({Key key,
this.onSaved,}) : super(key: key);
#override
_Record_WidgetState createState() => _Record_WidgetState();
}
class _Record_WidgetState extends State<Record_Widget> {
Directory appDirectory;
String records = '';
RecordingState _recordingState = RecordingState.UnSet;
// Recorder properties
FlutterAudioRecorder2 audioRecorder;
#override
void initState() {
super.initState();
FlutterAudioRecorder2.hasPermissions.then((hasPermission) {
if (hasPermission) {
_recordingState = RecordingState.Set;
}
});
}
#override
void dispose() {
_recordingState = RecordingState.UnSet;
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(100.0),
child: _RecordOrStopButton());
}
Container _RecordOrStopButton() {
return Container(
height: 90,
width: 90,
decoration: BoxDecoration(
border: Border.all(
width: 4.0,
color: Colors.grey,
),
shape: BoxShape.circle,
),
child: Padding(
padding: EdgeInsets.all(4.0),
child: isRecording == false ?
_createRecordButton() : _createStopButton()),);
}
//Widget Button Record
Container _createRecordButton({IconData icon, Function onPressFunc}) {
return Container(child: ElevatedButton(
onPressed: () async {
await _onRecordButtonPressed();
setState(() {
_recordingState = RecordingState.Recording;
isRecording = true;
});
},
style: ButtonStyle(
shape: MaterialStateProperty.all(CircleBorder()),
padding: MaterialStateProperty.all(EdgeInsets.all(20)),
backgroundColor: MaterialStateProperty.all(
Colors.red), // <-- Button color
)));
}
//Widget Button Stop
Container _createStopButton() {
return Container(
padding: EdgeInsets.all(16),
width: 30.0,
height: 30.0,
child: ElevatedButton(
onPressed: () {
_onRecordButtonPressed();
setState(() {
isRecording = false;
});
},
style: ButtonStyle(
fixedSize: MaterialStateProperty.all(Size(10, 10)),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(color: Colors.red))),
padding: MaterialStateProperty.all(EdgeInsets.all(20)),
backgroundColor: MaterialStateProperty.all(Colors.red),
),
));
}
Future<void> _onRecordButtonPressed() async {
switch (_recordingState) {
case RecordingState.Set:
await _recordVoice();
break;
case RecordingState.Recording:
await _stopRecording();
_recordingState = RecordingState.Stopped;
break;
case RecordingState.Stopped:
await _recordVoice();
break;
case RecordingState.UnSet:
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('Please allow recording from settings.'),
));
break;
}
}
_initRecorder() async {
Directory appDirectory = await getApplicationDocumentsDirectory();
String filePath = appDirectory.path +
'/' +
DateTime.now().millisecondsSinceEpoch.toString() +
'.aac';
audioRecorder =
FlutterAudioRecorder2(filePath, audioFormat: AudioFormat.AAC);
await audioRecorder.initialized;
}
_startRecording() async {
isRecording = true;
await audioRecorder.start();
await audioRecorder.current(channel: 0);
}
_stopRecording() async {
isRecording = false;
await audioRecorder.stop();
widget.onSaved();
}
Future<void> _recordVoice() async {
final hasPermission = await FlutterAudioRecorder2.hasPermissions;
if (hasPermission ?? false) {
await _initRecorder();
await _startRecording();
_recordingState = RecordingState.Recording;
} else {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('Please allow recording from settings.'),
));
}
}
}

Multiple showDialog for validation

On my project I need to use several showDialog one after the other.
For user creation, I use a SearchField widget to retrieve info from a table related to the user.
If the SearchField value does not exist I would like to propose the creation. Depending on the choice either the form is in error or I propose to register the user.
For this I use a showDialog in the validator of the SearchField and an if validator is correct.
My problem is that my second dialog box is displayed before validating the first one and even above that of the SearchField.
What is the correct way to do this?
Thank you,
class InformationsPage extends StatefulWidget {
const InformationsPage({
required Key key,
required this.user,
required this.type,
}) : super(key: key);
final User user;
final FenType type;
#override
InformationsPageState createState() => InformationsPageState();
}
class InformationsPageState extends State<InformationsPage>
with AutomaticKeepAliveClientMixin {
InformationsPageState({this.user});
final User? user;
late UserApi _api;
#override
bool get wantKeepAlive => true;
bool _familyIsCreated = false;
late User userSaved;
late FenType type;
//Info Form
var _pseudoController = TextEditingController();
var _familyController = TextEditingController();
#override
void initState() {
super.initState();
_api = UserApi();
_pseudoController = TextEditingController(text: widget.user.pseudo);
_familyController = TextEditingController(text: widget.user.familyName);
userSaved = User.fromUser();
type = widget.type;
}
#override
void dispose() {
_pseudoController.dispose();
_familyController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
super.build(context);
return Column(
children: <Widget>[
FutureBuilder(
future: _api.getFamilies(),
builder: (context, AsyncSnapshot<List<Family>> snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(
"Something wrong with message: ${snapshot.error.toString()}"));
} else if (snapshot.connectionState == ConnectionState.done) {
List<Family> _list = snapshot.data!;
return _buildDropdownSearchFamilies(_list);
} else {
return const Center(child: CircularProgressIndicator());
}
}),
TextFormField(
readOnly: type == FenType.read ? true : false,
inputFormatters: [LowerCaseTextFormatter()],
controller: _pseudoController,
onSaved: (value) => userSaved.pseudo = value,
decoration: const InputDecoration(
icon: Icon(Icons.person),
hintText: 'Pseudo',
labelText: 'Pseudo',
),
validator: (value) =>
value!.isEmpty ? 'Obligatory' : null),
],
);
}
int? _contains(List<Family> list, String? name) {
int? res = -1;
for (Family element in list) {
if (element.name == name) {
res = element.id;
break;
}
}
return res;
}
Widget _buildDropdownSearchFamilies(List<Family> _list) {
return SearchField(
controller: _familyController,
suggestions: _list
.map((e) =>
SearchFieldListItem(e.name!, child: Text(e.name!), item: e.id))
.toList(),
hint: 'Family',
validator: (x) {
if (x!.isEmpty) {
userSaved.familyId = null;
userSaved.familyName = null;
return null;
}
int? id = _contains(_list, x);
if (id == -1) {
userSaved.familyId == null;
showDiaglog(x);
if (userSaved.familyId != null) {
return null;
} else {
return 'Family not exist';
}
} else {
userSaved.familyId = id;
userSaved.familyName = x;
return null;
}
},
searchInputDecoration: const InputDecoration(
labelText: 'Family', icon: Icon(Icons.groups)),
itemHeight: 50,
onTap: (x) {
userSaved.familyId = x.item as int?;
userSaved.familyName = x.child.toString();
});
}
showDiaglog(String family) async {
String title = "Family";
String message =
"Family $family not exist. Create ?";
String textKoButton = "no";
String textOkButton = "yes";
MyDialog alert = MyDialog(
title: title,
message: message,
onPressedKo: koButtonPressed(),
onPressedOk: okButtonPressed(family),
textKoButton: textKoButton,
textOkButton: textOkButton);
await showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
void Function() koButtonPressed() => () {
_familyIsCreated = false;
Navigator.of(context).pop(false);
};
void Function() okButtonPressed(family) => () {
_save(family);
Navigator.of(context).pop();
};
void _save(family) async {
UserApi apiUser = UserApi();
Family oldF = Family.empty();
Family newF = Family.empty();
newF.name = family;
newF.createdAt = oldF.createdAt;
newF.deletedAt = newF.deletedAt;
Map<String, dynamic> data = oldF.toJson(newF);
int res = -1;
res = await apiUser.createFamily(data);
SnackBar snackBar;
if (res != -1) {
snackBar = MyWidget.okSnackBar('Family created');
userSaved.familyId = res;
userSaved.familyName = family;
} else {
snackBar = MyWidget.koSnackBar(
'Family not created');
userSaved.familyId = null;
userSaved.familyName = null;
}
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
My form :
class UserFormPage extends StatefulWidget {
static const String routeName = '/admin/user-form';
final User? user;
final FenType fenType;
const UserFormPage({Key? key, required this.user, required this.fenType})
: super(key: key);
#override
_UserFormPageState createState() => _UserFormPageState();
}
class _UserFormPageState extends State<UserFormPage>
with SingleTickerProviderStateMixin {
static final GlobalKey<FormState> _formKey =
GlobalKey<FormState>(debugLabel: '_appState');
static final GlobalKey<InformationsPageState> _infoKey =
GlobalKey<InformationsPageState>();
late TabController _controller;
late User _user;
late User _userSaved;
#override
void initState() {
super.initState();
_controller = TabController(vsync: this, length: 2);
_user = widget.user!;
_userSaved = widget.user!;
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () =>
Navigator.pushReplacementNamed(context, Routes.admUserList),
),
title: const Text('Member'),
actions: <Widget>[
Visibility(
visible: widget.fenType != FenType.read ? true : false,
child: IconButton(
icon: const Icon(Icons.save),
onPressed: () {
if (!_formKey.currentState!.validate()) {
return;
}
showDiaglog();
},
))
],
bottom: TabBar(
controller: _controller,
tabs: const [
Tab(text: 'Info'),
Tab(text: 'Others'),
],
),
),
body: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Flexible(
child: TabBarView(
controller: _controller,
children: <Widget>[
SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InformationsPage(
user: _user,
key: _infoKey,
type: widget.fenType),
])),
SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DetailsPage(
user: _user,
key: _detailsKey,
type: widget.fenType)
],
)),
],
))
],
))),
);
}
void _save() async {
final infoState = _infoKey.currentState;
_userSaved = infoState?.userSaved ?? _user;
_userSaved.pseudo = infoState?.userSaved.pseudo ?? _user.pseudo;
Map<String, dynamic> data = _user.userToJsonClean(_userSaved);
if (!_userSaved.userIsUpdated()) {
final outSnackBar = MyWidget.okSnackBar('Not update');
ScaffoldMessenger.of(context).showSnackBar(outSnackBar);
} else {
UserApi apiUser = UserApi();
bool res = false;
res = widget.fenType == FenType.update
? await apiUser.update(data)
: await apiUser.create(data);
SnackBar snackBar;
res
? snackBar = MyWidget.okSnackBar('Member saved')
: snackBar = MyWidget.koSnackBar(
'Member not saved');
ScaffoldMessenger.of(context).showSnackBar(snackBar);
_user = _userSaved;
if (widget.fenType == FenType.create) {
Navigator.of(context).popAndPushNamed(Routes.admUserList);
}
}
}
void showDiaglog() {
String pseudo = _userSaved.pseudo!;
String title = "Save";
String message = widget.fenType == FenType.create
? "Create member $pseudo ?"
: "Save meber $pseudo ?";
String textKoButton = "no";
String textOkButton = "yes";
MyDialog alert = MyDialog(
title: title,
message: message,
onPressedKo: koButtonPressed(),
onPressedOk: okButtonPressed(),
textKoButton: textKoButton,
textOkButton: textOkButton);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
void Function() koButtonPressed() => () {
Navigator.of(context).pop(false);
};
void Function() okButtonPressed() => () {
_formKey.currentState!.save();
_save();
Navigator.of(context).pop();
};
}
I resolve this problem to modified the widget SearchField to a DropdownSearch.

Slider widget does not work on iOS and Web

I have an audio player in my app. I used Slider.adaptive to make the seek bar. Although It works well on Android, it gives me this error on iOS and the Web:
════════ Exception caught by widgets library
═══════════════════════════════════ Assertion failed:
../…/cupertino/slider.dart:332 value != null && value >= 0.0 && value
<= 1.0 is not true
The relevant error-causing widget was Slider
Here is how my code looks like:
class PlayBackButtons extends StatefulWidget {
#override
_PlayBackButtonsState createState() => _PlayBackButtonsState();
}
class _PlayBackButtonsState extends State<PlayBackButtons> {
bool _isPlaying = false;
AudioPlayer _audioPlayer;
Duration _duration = new Duration();
Duration _position = new Duration();
int result = 0;
int tutsLenght;
int index;
#override
void initState() {
super.initState();
_audioPlayer = AudioPlayer();
Future.delayed(Duration.zero, () {
Provider.of<Tutorials>(context, listen: false).resetIndex();
});
}
#override
void dispose() {
_cleanup();
super.dispose();
}
double progress() => max() > (_position?.inMilliseconds ?? 0).toDouble()
? (_position?.inMilliseconds ?? 0).toDouble()
: 0.0;
double max() => (_duration.inMilliseconds ?? 0.0).toDouble();
void _cleanup() {
if (result == 1) {
_audioPlayer.stop();
_audioPlayer.dispose();
Provider.of<Tutorials>(context).resetIndex();
}
}
void _play(String url) async {
result = await _audioPlayer.play(url);
setState(() => _isPlaying = true);
if (result == 1) {
_audioPlayer.onDurationChanged.listen((Duration dd) {
setState(() {
_duration = dd;
});
});
_audioPlayer.onAudioPositionChanged.listen((Duration dd) {
setState(() {
_position = dd;
});
});
} else {
print('audio player crashed');
}
}
void _pause() async {
await _audioPlayer.pause();
setState(() => _isPlaying = false);
}
void _next() async {
await _audioPlayer.stop();
setState(() => _isPlaying = false);
if (Provider.of<Tutorials>(context).index < tutsLenght) {
} else {
Navigator.pop(context);
}
}
void _previous() {
if (Provider.of<Tutorials>(context).index != 0) {
_audioPlayer.stop();
setState(() => _isPlaying = false);
Provider.of<Tutorials>(context).decrementIndex();
}
}
#override
Widget build(BuildContext context) {
index = Provider.of<Tutorials>(context).index;
final tuts = Provider.of<Tutorials>(context, listen: false).tutsOfASection;
tutsLenght = tuts.length;
final url = tuts[index].audio;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Slider.adaptive(
min: 0.0,
value: progress(),//_position.inSeconds.toDouble(),
max: max(),//_duration.inSeconds.toDouble(),
onChanged: (double value) {
setState(() => _audioPlayer.seek(Duration(seconds: value.toInt())));
},
),
Row(
children: [
IconButton(
icon: Icon(Icons.skip_previous),
onPressed: () => _previous(),
),
IconButton(
icon: _isPlaying ? Icon(Icons.pause) : Icon(Icons.play_arrow),
onPressed: () {
if (_isPlaying) {
_pause();
} else {
_play(url);
}
},
),
IconButton(
icon: Icon(Icons.skip_next),
onPressed: () => _next(),
),
],
mainAxisAlignment: MainAxisAlignment.center,
),
],
);
}
}

Disabled button when textInputField is invalid on change text Flutter

I'm new on Flutter.
I'm trying to disabled button while I compile textFormField and it is invalid.
My problem is that it works only if I click on "confirm" on keyboard and not while I compile my input.
So would like disable button while I write the input.
I've done a pastebin for example:
https://pastebin.com/q5WuwrCm
class AddCartButton extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return AddCartButtonState();
}
}
class AddCartButtonState extends State<AddCartButton>{
TextEditingController myController = TextEditingController();
bool isValid = false;
#override
Widget build(BuildContext context) {
void _addToCart(){
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("QUANTITY"),
content: Column(
children: <Widget>[
TextFormField(
controller: myController,
decoration: new InputDecoration(labelText: "quantity"),
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: <TextInputFormatter>[],
autovalidate: true,
validator: (value) {
if (value.isEmpty) {
isValid = false;
return "the quantity cannot be empty";
} else if (double.tryParse(value) == null) {
isValid = false;
return "the quantity must be valid number";
} else {
isValid = true;
return null;
}
}
)
],
),
actions: <Widget>[
FlatButton(
disabledTextColor: Colors.grey,
child: Text("add"),
onPressed: isValid ? () { print("is valid"); }: null
)
],
);
},
);
}
}
}
You can use addListener() function on your TextEditingController myController.
myController.addListener((){
//With this, you can "listen" all the changes on your text while
//you are typing on input
print("value: ${myController.text}");
//use setState to rebuild the widget
setState(() {
//you can check here if your text is valid or no
//_isValidText() is just an invented function that returns
//a boolean representing if the text is valid or not
if(_isValidText(myController.text)) isValid = true;
else isValid = false;
});
});
If I understood what you want to achieve the following code works for you. The conditions I set are the same as your validation rules:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
TextEditingController _textEditingController = TextEditingController();
Function _submitFunction;
// We need a reusable timer and it needs to not be null so that we can cancel it from th start.
Timer _timer = Timer(Duration(milliseconds: 1), () => print('initialTimer'));
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Auto disable button'),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
controller: _textEditingController,
onChanged: (text) {
setState(() {
_submitFunction = null;
});
restartTimer();
},
),
RaisedButton(
child: Text('Submit'),
onPressed: _submitFunction,
),
],
),
),
)
);
}
void submitForm(){
print(_textEditingController.text);
}
void enableButton(){
if(_textEditingController.text != ''){
setState(() {
_submitFunction = submitForm;
});
}
}
void restartTimer(){
_timer.cancel();
_timer = Timer(Duration(milliseconds: 1500), enableButton);
}
}
You can use this method .
bool isEnable = false;
void validateButton() {
bool isValid = true;
isValid = userEmail.isNotEmpty &&
userPassword.isNotEmpty &&
validateEmail(userEmail) &&
userPassword.length >= 8;
setState(() {
isEnable = isValid;
});
}
now in your Textfield onChanged method you have to call this function
like This
onChanged: (email) {
userEmail = email;
setState(() {});
validateButton();
},
and in your Login Button
isEnable?ActiveButton():DisableButton()