Недавно я написал нужную мне тестовую программу, которая по сути является программой CRUD. Мне нужно было обработать это иначе, чем другим аналогичным программам, которые я написал, потому что я обычно использую виджет FAB с состоянием и не должен устанавливать setState () для включения и отключения FAB. В этой тестовой программе я не хотел использовать пользовательский FAB, а использовал стандартный FAB. Я обнаружил, что всякий раз, когда мне приходилось включать или отключать FAB из-за изменения TextField, для этого требовался setState (), и после сборки курсор для редактируемого TextField перемещался. Я не знаю, почему это происходит, потому что я не воссоздал виджеты. Единственное решение, которое я мог найти для решения этой проблемы, было довольно грязным и требовало сохранения позиции виджета в списке TextField, а также сохранения выделения, а затем после сборки сбрасывать выделение в сохраненное выделение.
Мне нужно, чтобы FAB включался только при изменении данных. Очевидно, что это может варьироваться с каждой ключевой записью.
Полагаю, я не справляюсь с этим оптимальным образом. Как это сделать, чтобы позиция курсора осталась такой же, какой была до сборки?
----- Добавили код ниже ----
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
void main() => runApp(MyApp());
//=====================================================================================
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Test Widgets',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(title: 'Test Widgets'),
);
}
}
//=====================================================================================
class HomePage extends StatefulWidget {
HomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_HomePageState createState() => _HomePageState();
}
//=====================================================================================
class _HomePageState extends State<HomePage> {
bool _tfDataHasChanged = false;
bool _tfInitialized = false;
bool _tfSaveSelection = false;
int _iNdxWidgetChanged = -1;
List<String> _lsOldData = ['Row 1', 'Row 2', 'Row 3', 'Row 4'];
List<String> _lsNewData = ['Row 1', 'Row 2', 'Row 3', 'Row 4'];
List<TextField> _lwTextFields;
TextSelection _wTextSelection;
//-------------------------------------------------------------------------------------
@override
void dispose() {
for (int iNdxWidget = 0;
_lwTextFields != null && iNdxWidget < _lwTextFields.length;
iNdxWidget++) {
_lwTextFields[iNdxWidget].focusNode.removeListener(() {
_fnFocusChanged();
});
_lwTextFields[iNdxWidget]?.controller?.dispose();
_lwTextFields[iNdxWidget]?.focusNode?.dispose();
}
super.dispose();
}
//-------------------------------------------------------------------------------------
@override
Widget build(BuildContext context) {
_tfInitialized = false;
SchedulerBinding.instance.addPostFrameCallback((_) => _fnOnBuildComplete());
if (_lwTextFields == null) {
_fnCreateAllWidgets();
}
List<Widget> lwDisplay = _fnCreateDisplay();
return Scaffold(
appBar: AppBar(
flexibleSpace: SafeArea(
child: _fnCreateAppBarWidgets(),
)),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: lwDisplay,
),
),
floatingActionButton: FloatingActionButton(
onPressed: _tfDataHasChanged ? _fnUpdateData : null,
tooltip: 'Update',
backgroundColor: _tfDataHasChanged ? Colors.blue : Colors.grey,
child: Icon(Icons.done),
),
);
}
//-------------------------------------------------------------------------------------
_fnOnBuildComplete() {
_tfInitialized = true;
if (_tfSaveSelection && _iNdxWidgetChanged >= 0) {
_lwTextFields[_iNdxWidgetChanged].controller.selection = _wTextSelection;
}
}
//-------------------------------------------------------------------------------------
void _fnCreateAllWidgets() {
_lwTextFields = List(_lsNewData.length);
for (int iNdxWidget = 0; iNdxWidget < _lwTextFields.length; iNdxWidget++) {
_fnCreateTextField(iNdxWidget);
}
}
//-------------------------------------------------------------------------------------
void _fnCreateTextField(int iNdxWidget) {
TextEditingController wController = TextEditingController();
FocusNode wFocusNode = FocusNode();
wFocusNode.addListener(() => _fnFocusChanged());
_lwTextFields[iNdxWidget] = TextField(
autofocus: false, //(iNdxWidget == 0),
autocorrect: false,
enabled: true,
keyboardType: TextInputType.text,
maxLength: 25,
controller: wController,
focusNode: wFocusNode,
textInputAction: TextInputAction.next /* TYPE OF ACTION KEY */,
onSubmitted: ((v) => _fnSetNextFocus(iNdxWidget)),
onChanged: (text) => _fnTextListener(iNdxWidget, text),
decoration: _fnCreateInputDecoration(
'Text Field Number ${iNdxWidget + 1}', 'Enter Data'),
style: _fnCreateWidgetTextStyle(Colors.blue[700]),
);
}
//-------------------------------------------------------------------------------------
_fnTextListener(int iNdxWidget, String sText) {
if (_tfInitialized) {
_lsNewData[iNdxWidget] = sText;
_fnCheckIfDataHasChanged(
iNdxWidget) /* ENABLE OR DISABLE SUBMIT BUTTON */;
}
}
//-------------------------------------------------------------------------------------
_fnSetNextFocus(int iNdxWidget) {
if (_lwTextFields[iNdxWidget].focusNode.hasFocus) {
_lwTextFields[iNdxWidget].focusNode.unfocus();
if (iNdxWidget + 1 < _lwTextFields.length) {
_lwTextFields[iNdxWidget + 1]?.focusNode?.requestFocus();
}
}
}
//-------------------------------------------------------------------------------------
InputDecoration _fnCreateInputDecoration(String sHeading, String sHint) {
return InputDecoration(
labelText: sHeading,
hintText: sHint,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(20.0)),
);
}
//-------------------------------------------------------------------------------------
TextStyle _fnCreateWidgetTextStyle(Color color) {
return TextStyle(
fontSize: 14.0,
color: color,
);
}
//-------------------------------------------------------------------------------------
List<Widget> _fnCreateDisplay() {
List<Widget> lwDisplay = List((_lwTextFields.length * 2) + 2);
lwDisplay[0] = SizedBox(height: 10);
int iNdxDisplay = 1;
for (int iNdxWidget = 0; iNdxWidget < _lwTextFields.length; iNdxWidget++) {
_lwTextFields[iNdxWidget].controller.text = _lsNewData[iNdxWidget];
lwDisplay[iNdxDisplay++] = _lwTextFields[iNdxWidget];
lwDisplay[iNdxDisplay++] =
SizedBox(height: iNdxDisplay < lwDisplay.length - 2 ? 10 : 80);
}
lwDisplay[lwDisplay.length - 1] = Divider(color: Colors.black, height: 2);
return lwDisplay;
}
//-------------------------------------------------------------------------------------
_fnUpdateData() {
for (int iNdxWidget = 0; iNdxWidget < _lsNewData.length; iNdxWidget++) {
if (_lsNewData[iNdxWidget] != _lsOldData[iNdxWidget]) {
_lsOldData[iNdxWidget] = _lsNewData[iNdxWidget];
}
}
_fnCheckIfDataHasChanged(-1);
}
//-------------------------------------------------------------------------------------
_fnCheckIfDataHasChanged(int iNdxWidgetChanged) {
bool tfChanged = false /* INIT */;
for (int iNdxWidgetTest = 0;
!tfChanged && iNdxWidgetTest < _lsNewData.length;
iNdxWidgetTest++) {
tfChanged = _lsNewData[iNdxWidgetTest] != _lsOldData[iNdxWidgetTest];
}
if (iNdxWidgetChanged >= 0) {
_iNdxWidgetChanged = iNdxWidgetChanged;
_wTextSelection = _lwTextFields[iNdxWidgetChanged].controller.selection;
}
if (tfChanged != _tfDataHasChanged) {
setState(() => _tfDataHasChanged = tfChanged) /* WE NEED TO ENABLE FAB */;
}
}
//-------------------------------------------------------------------------------------
Row _fnCreateAppBarWidgets() {
IconData wIconData =
_tfSaveSelection ? Icons.check_box : Icons.check_box_outline_blank;
Color wColor = _tfSaveSelection ? Colors.blue[900] : Colors.grey[600];
IconButton wIconButton = IconButton(
icon: Icon(wIconData),
color: wColor,
onPressed: _fnCheckboxChanged,
iconSize: 40);
return Row(children: <Widget>[
SizedBox(width: 10),
Text('Save\nSelection', textAlign: TextAlign.center),
wIconButton,
SizedBox(width: 30),
Text('Test TextField')
]);
}
//-------------------------------------------------------------------------------------
_fnFocusChanged() {
for (int iNdxWidget = 0; iNdxWidget < _lwTextFields.length; iNdxWidget++) {
if (_lwTextFields[iNdxWidget].focusNode.hasFocus) {
_iNdxWidgetChanged = iNdxWidget;
_wTextSelection = _lwTextFields[iNdxWidget].controller.selection;
return;
}
}
}
//-------------------------------------------------------------------------------------
void _fnCheckboxChanged() {
_tfSaveSelection = !_tfSaveSelection;
if (!_tfSaveSelection) {
_iNdxWidgetChanged = -1;
}
setState(() {});
}
}
--- ----- Добавили ключ к TextField, но проблема сохраняется ---------
key: ValueKey<int>(iNdxWidget),