#flutter #dart
#флаттер #дротик
Вопрос:
У меня есть один повторно используемый компонент ввода текста, который имеет состояние.Я использую этот компонент внутри экранной формы с полным состоянием, где я пытаюсь обновить свое поле ввода, но не работает.
Мой компонент TextInput.
import 'package:flutter/material.dart';
class TextInputField extends StatefulWidget {
final String label;
final TextInputType keyboardType;
final Function onInput;
final Function validator;
final value;
final Function onTap;
final Function onSaved;
final readOnly;
final bool showClearIcon;
final Function onClearTap;
final Function setValue;
TextInputField(
{@required this.label,
this.onInput,
this.validator,
this.keyboardType = TextInputType.text,
this.value,
this.onTap,
this.onSaved,
this.readOnly = false,
this.showClearIcon = false,
this.onClearTap,
this.setValue});
@override
_TextInputFieldState createState() => _TextInputFieldState();
}
class _TextInputFieldState extends State<TextInputField> {
final TextEditingController controller = TextEditingController();
String storedValue;
@override
void initState() {
controller.text = widget.value;
super.initState();
}
@override
Widget build(BuildContext context) {
storedValue = widget.value.toString();
return Padding(
padding: const EdgeInsets.only(top: 15),
child: TextFormField(
key: Key(widget.label),
readOnly: widget.readOnly,
controller: controller,
onChanged: widget.onInput,
onTap: widget.onTap,
onSaved: widget.onSaved,
validator: widget.validator,
// initialValue: widget.value ?? '',
keyboardType: this.widget.keyboardType,
textInputAction: this.widget.keyboardType == TextInputType.multiline
? TextInputAction.newline
: null,
maxLines: null,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(
const Radius.circular(10.0),
),
),
filled: true,
hintStyle: TextStyle(color: Colors.grey[800]),
labelText: this.widget.label,
fillColor: Colors.white70,
alignLabelWithHint: true,
isDense: true,
suffixIcon: widget.showClearIcon
? InkWell(
onTap: widget.onClearTap,
child: Icon(Icons.close),
)
: null,
),
textCapitalization: TextCapitalization.sentences,
),
);
}
}
Мой экран / форма
import 'dart:async';
import 'package:edu_assist/widgets/buttons/form_buttons.dart';
import 'package:edu_assist/widgets/inputs/input_text_field.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class AddBranch extends StatefulWidget {
@override
_AddBranchState createState() => _AddBranchState();
}
class _AddBranchState extends State<AddBranch> {
final _formKey = GlobalKey<FormState>();
String mapKey = DotEnv().env['MAP_KEY'];
Completer<GoogleMapController> _controller = Completer();
Position position;
static final CameraPosition _kMumbaiLocation = CameraPosition(
target: LatLng(19.04665, 74.8470429),
zoom: 14.4746,
);
Future<void> _getMyLocation() async {
Position p = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
setState(() {
position = p;
_setNewMapLocation(p);
});
}
Future<void> _setNewMapLocation(Position p) async {
var newPostion = CameraPosition(
target: LatLng(p.latitude, p.longitude),
zoom: 19.151926040649414,
);
final GoogleMapController controller = await _controller.future;
controller.animateCamera(CameraUpdate.newCameraPosition(newPostion));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add New Branch'),
),
body: SingleChildScrollView(
child: Container(
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(15),
child: Column(
children: [
TextInputField(
label: 'Branch Name*',
value: 'sds',
onSaved: (String val) {},
),
TextInputField(
label: 'Lattitude',
value:
position == null ? null : position.latitude.toString(),
onSaved: (String val) {},
),
TextInputField(
label: 'Longitude',
value:
position == null ? null : position.longitude.toString(),
onSaved: (String val) {},
),
SizedBox(
height: 20,
),
Container(
height: 150,
child: GoogleMap(
myLocationButtonEnabled: true,
myLocationEnabled: true,
initialCameraPosition: _kMumbaiLocation,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
),
),
SizedBox(
height: 10,
),
RaisedButton.icon(
onPressed: () {
_getMyLocation();
},
icon: Icon(Icons.location_on),
label: Text("Use My Current Location"),
),
SizedBox(
height: 20,
),
FormButtons(
onSuccessTap: () {},
)
],
),
),
),
),
),
);
}
}
Здесь, когда я нажимаю кнопку «Использовать мое текущее местоположение» и хочу получить текущее местоположение пользователя и показать его в полях ввода широты и долготы.Я обновляю состояние, используя setState
метод для установки Position
, но состояние поля ввода не обновляется.
Я предполагаю, что это связано с тем, как я обрабатываю начальное значение в компоненте, вызывающем проблему.
Ответ №1:
При использовании setState
виджеты с отслеживанием состояния внутри состояния будут вызывать метод сборки, а не initState
метод. Ваша проблема в том, что вы устанавливаете значение контроллера в initState
функции, по этой причине значение не обновляется. Решение вашей проблемы — передать TextEditingController
в TextInputField
.
///Check another time if you really want a stateful widget here
class TextInputField extends StatefulWidget {
...
...
TextEditingController controller;
...
...
}
Затем вам нужно изменить значение position, которое вы устанавливаете для значения контроллера следующим образом
Future<void> _getMyLocation() async {
Position p = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
//you don't need setState to update the textfield, controller will
// take care of that
position = p;
_postionTextController.text=p;
}
Примечание: если вы создаете свой пользовательский виджет текстового поля, не создавайте контроллер внутри виджета, вместо этого передайте его виджету среди атрибутов, аналогичных способу TextField
и TextFormField
do
Комментарии:
1. Спасибо @Sami Kanafani. Ваше предложение сработало. Я ценю ваше мнение. В моем приложении есть множество форм с 10-20 полями ввода, поэтому я подумал о создании контроллера внутри компонента, который упрощает установку начального значения, иначе мне придется создавать множество контроллеров. Вы предлагаете какой-нибудь лучший способ избежать создания большого количества контроллеров?
2. Поскольку вы используете
TextFormField
, вы можете использовать атрибутonChange
, таким образом, вам не нужно будет создавать контроллер для каждого текстового поля. В дополнение к этому и просто небольшое напоминание, не забудьте удалить контроллеры после переопределения метода dispose состояния