Flutter:如何将其他值(对象)分配为另一个字段中的文本编辑控制器?
Flutter: How to assign other value (object) as Text Editing Controller in Another Field?
我正在构建一个包含联系人姓名和 phone 号码字段的表单。用户将能够从以前保存的联系人列表中选择(点击)联系人,这应该会在各自的字段中显示姓名和 phone 号码。
为了实现这一点,我使用 Flutter_Form_Builder 包版本:3.14.0 中的 TypeAheadFormField 来构建我的表单。
我在默认的 TypeAheadFormField 控制器中成功地从本地数据库分配了 _nameController。
但是我无法从我点击到 FormBuilderTextField 的相同选择中分配 _mobileController。
我设法通过 TypeAheadFormField 获得“名称”值,但每次我从建议中切换选项时,
_mobileController.text 没有在 FormBuilderTextField
上更新
我的代码如下:
import 'package:myApp/customer.dart';
import 'package:myApp/db_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
class MyForm extends StatefulWidget {
@override
_MyFormState createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
DatabaseHelper _dbHelper;
Customer _customer = Customer();
List<Customer> _customerList = [];
final _formKey = GlobalKey<FormBuilderState>();
final _cfKey = GlobalKey<FormBuilderState>();
final _nameController = TextEditingController();
final _inputContactNameController = TextEditingController();
final _inputContactPhoneController = TextEditingController();
var _mobileController = TextEditingController();
@override
void initState() {
super.initState();
_refreshBikeSellerList();
setState(() {
_dbHelper = DatabaseHelper.instance;
});
_mobileController = TextEditingController();
_mobileController.addListener(() {
setState(() {});
});
}
@override
void dispose() {
_mobileController.dispose();
_nameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
child: FormBuilder(
key: _formKey,
child: Column(
children: [
FormBuilderTypeAhead(
attribute: 'contact_person',
initialValue: _customer,
controller: _nameController,
onChanged: (val) {},
itemBuilder: (context, Customer _customer) {
return ListTile(
title: Text(_customer.name),
subtitle: Text(_customer.mobile),
);
},
selectionToTextTransformer: (Customer c) => c.name,
suggestionsCallback: (query) {
if (query.isNotEmpty) {
var lowercaseQuery = query.toLowerCase();
return _customerList.where((_customer) {
return _customer.name
.toLowerCase()
.contains(lowercaseQuery);
}).toList(growable: false)
..sort((a, b) => a.name
.toLowerCase()
.indexOf(lowercaseQuery)
.compareTo(
b.name.toLowerCase().indexOf(lowercaseQuery)));
} else {
return _customerList;
}
},
textFieldConfiguration: TextFieldConfiguration(
autofocus: true,
style: DefaultTextStyle.of(context).style.copyWith(
fontSize: 17,
letterSpacing: 1.2,
color: Colors.black,
fontWeight: FontWeight.w300,
),
// controller: guessMotor1,
),
onSuggestionSelected: (val) {
if (val != null) {
return _customerList.map((_customer) {
setState(() {
_mobileController.text = _customer.mobile;
});
}).toList();
} else {
return _customerList;
}
},
),
FormBuilderTextField(
controller: _mobileController,
attribute: 'mobile',
readOnly: true,
style: TextStyle(fontSize: 17),
decoration: InputDecoration(
hintText: 'mobile',
),
),
SizedBox(height: 40),
Container(
child: RaisedButton(
onPressed: () async {
await manageContact(context);
},
child: Text('Manage Contact'),
),
),
],
),
),
);
}
manageContact(BuildContext context) async {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(
'Manage Contact',
textAlign: TextAlign.center,
),
titleTextStyle: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 17,
color: Colors.black45,
letterSpacing: 0.8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12))),
content: FormBuilder(
key: _cfKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// SizedBox(height: 10),
InkResponse(
onTap: () {},
child: CircleAvatar(
radius: 30,
child: Icon(
Icons.person_add,
color: Colors.grey[100],
),
backgroundColor: Colors.grey[500],
),
),
SizedBox(height: 10),
Container(
width: MediaQuery.of(context).size.width * 0.5,
margin: EdgeInsets.symmetric(horizontal: 15),
child: FormBuilderTextField(
maxLength: 20,
controller: _inputContactNameController,
textAlign: TextAlign.start,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.words,
attribute: 'contact_person',
decoration: InputDecoration(
prefixIcon: Icon(
Icons.person_outline,
size: 22,
)),
onChanged: (val) {
setState(() {
_customer.name = val;
_formKey
.currentState.fields['contact_person'].currentState
.validate();
});
},
autovalidateMode: AutovalidateMode.always,
validators: [
FormBuilderValidators.required(),
FormBuilderValidators.maxLength(20),
FormBuilderValidators.minLength(2),
],
),
),
Container(
width: MediaQuery.of(context).size.width * 0.5,
margin: EdgeInsets.symmetric(horizontal: 15),
child: FormBuilderTextField(
attribute: 'phone_number',
controller: _inputContactPhoneController,
textAlign: TextAlign.start,
keyboardType: TextInputType.number,
decoration: InputDecoration(
prefixIcon: Icon(
Icons.phone_android,
size: 22,
)),
onChanged: (val) {
setState(() {
_customer.mobile = val;
_formKey.currentState.fields['phone_number'].currentState
.validate();
});
},
validators: [
FormBuilderValidators.required(),
FormBuilderValidators.numeric(),
],
valueTransformer: (text) {
return text == null ? null : num.tryParse(text);
},
),
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
RaisedButton(
color: Colors.white,
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
}),
RaisedButton(
color: Colors.grey[400],
child: Text(
'Save',
style: TextStyle(color: Colors.white),
),
onPressed: () async {
try {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
if (_customer.id == null)
await _dbHelper.insertBikeContact(_customer);
else
await _dbHelper.updateCustomer(_customer);
_refreshBikeSellerList();
_formKey.currentState.reset();
_inputContactNameController.clear();
_inputContactPhoneController.clear();
Navigator.of(context).pop();
}
} catch (e) {
print(e);
}
},
)
],
),
],
),
),
),
);
}
_refreshBikeSellerList() async {
List<Customer> x = await _dbHelper.getCustomer();
setState(() {
_customerList = x;
});
}
}
有什么方法可以在我点击时更新 _mobileController 吗?
如有任何帮助,我们将不胜感激。
提前谢谢你。
已编辑:
class 我保存客户数据的地方:
class Customer {
int id;
String name;
String mobile;
static const tblCustomer = 'Customer';
static const colId = 'id';
static const colName = 'name';
static const colMobile = 'mobile';
Customer({
this.id,
this.name,
this.mobile,
});
Map<String, dynamic> toMap() {
var map = <String, dynamic>{colName: name, colMobile: mobile};
if (id != null) map[colId] = id;
return map;
}
Customer.fromMap(Map<String, dynamic> map) {
id = map[colId];
name = map[colName];
mobile = map[colMobile];
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Customer &&
runtimeType == other.runtimeType &&
name == other.name;
@override
int get hashCode => name.hashCode;
@override
String toString() {
return name;
}
}
这是我的数据库:
import 'dart:async';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'customer.dart';
class DatabaseHelper {
static const _databaseVersion = 1;
static const _databaseName = 'Kiloin.db';
DatabaseHelper._();
static final DatabaseHelper instance = DatabaseHelper._();
Database _database;
Future<Database> get database async {
if (_database != null) return _database;
_database = await _initDatabase();
return _database;
}
_initDatabase() async {
Directory dataDirectory = await getApplicationDocumentsDirectory();
String dbPath = join(dataDirectory.path, _databaseName);
return await openDatabase(
dbPath,
version: _databaseVersion,
onCreate: _onCreateDB,
);
}
_onCreateDB(Database db, int version) async {
await db.execute('''
CREATE TABLE ${Customer.tblCustomer}(
${Customer.colId} INTEGER PRIMARY KEY AUTOINCREMENT,
${Customer.colName} TEXT NOT NULL,
${Customer.colMobile} TEXT NOT NULL
)
''');
}
Future<int> insertBikeContact(Customer customer) async {
Database db = await database;
return await db.insert(Customer.tblCustomer, customer.toMap());
}
Future<List<Customer>> getCustomer() async {
Database db = await database;
List<Map> contact = await db.query(Customer.tblCustomer);
return contact.length == 0
? []
: contact.map((e) => Customer.fromMap(e)).toList();
}
Future<int> updateCustomer(Customer customer) async {
Database db = await database;
return await db.update(Customer.tblCustomer, customer.toMap(),
where: '${Customer.colId}=?', whereArgs: [customer.id]);
}
Future<int> deleteContact(int id) async {
Database db = await database;
return await db.delete(Customer.tblCustomer,
where: '${Customer.colId}=?', whereArgs: [id]);
}
}
您从onSuggestionSelected
获得的价值就是客户。使用该值更新 _mobileController.text
.
onSuggestionSelected: (customer) {
if (customer != null) {
setState(() {
_mobileController.text = customer.mobile;
});
}
}
我正在构建一个包含联系人姓名和 phone 号码字段的表单。用户将能够从以前保存的联系人列表中选择(点击)联系人,这应该会在各自的字段中显示姓名和 phone 号码。
为了实现这一点,我使用 Flutter_Form_Builder 包版本:3.14.0 中的 TypeAheadFormField 来构建我的表单。
我在默认的 TypeAheadFormField 控制器中成功地从本地数据库分配了 _nameController。 但是我无法从我点击到 FormBuilderTextField 的相同选择中分配 _mobileController。
我设法通过 TypeAheadFormField 获得“名称”值,但每次我从建议中切换选项时, _mobileController.text 没有在 FormBuilderTextField
上更新我的代码如下:
import 'package:myApp/customer.dart';
import 'package:myApp/db_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
class MyForm extends StatefulWidget {
@override
_MyFormState createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
DatabaseHelper _dbHelper;
Customer _customer = Customer();
List<Customer> _customerList = [];
final _formKey = GlobalKey<FormBuilderState>();
final _cfKey = GlobalKey<FormBuilderState>();
final _nameController = TextEditingController();
final _inputContactNameController = TextEditingController();
final _inputContactPhoneController = TextEditingController();
var _mobileController = TextEditingController();
@override
void initState() {
super.initState();
_refreshBikeSellerList();
setState(() {
_dbHelper = DatabaseHelper.instance;
});
_mobileController = TextEditingController();
_mobileController.addListener(() {
setState(() {});
});
}
@override
void dispose() {
_mobileController.dispose();
_nameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
child: FormBuilder(
key: _formKey,
child: Column(
children: [
FormBuilderTypeAhead(
attribute: 'contact_person',
initialValue: _customer,
controller: _nameController,
onChanged: (val) {},
itemBuilder: (context, Customer _customer) {
return ListTile(
title: Text(_customer.name),
subtitle: Text(_customer.mobile),
);
},
selectionToTextTransformer: (Customer c) => c.name,
suggestionsCallback: (query) {
if (query.isNotEmpty) {
var lowercaseQuery = query.toLowerCase();
return _customerList.where((_customer) {
return _customer.name
.toLowerCase()
.contains(lowercaseQuery);
}).toList(growable: false)
..sort((a, b) => a.name
.toLowerCase()
.indexOf(lowercaseQuery)
.compareTo(
b.name.toLowerCase().indexOf(lowercaseQuery)));
} else {
return _customerList;
}
},
textFieldConfiguration: TextFieldConfiguration(
autofocus: true,
style: DefaultTextStyle.of(context).style.copyWith(
fontSize: 17,
letterSpacing: 1.2,
color: Colors.black,
fontWeight: FontWeight.w300,
),
// controller: guessMotor1,
),
onSuggestionSelected: (val) {
if (val != null) {
return _customerList.map((_customer) {
setState(() {
_mobileController.text = _customer.mobile;
});
}).toList();
} else {
return _customerList;
}
},
),
FormBuilderTextField(
controller: _mobileController,
attribute: 'mobile',
readOnly: true,
style: TextStyle(fontSize: 17),
decoration: InputDecoration(
hintText: 'mobile',
),
),
SizedBox(height: 40),
Container(
child: RaisedButton(
onPressed: () async {
await manageContact(context);
},
child: Text('Manage Contact'),
),
),
],
),
),
);
}
manageContact(BuildContext context) async {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(
'Manage Contact',
textAlign: TextAlign.center,
),
titleTextStyle: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 17,
color: Colors.black45,
letterSpacing: 0.8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12))),
content: FormBuilder(
key: _cfKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// SizedBox(height: 10),
InkResponse(
onTap: () {},
child: CircleAvatar(
radius: 30,
child: Icon(
Icons.person_add,
color: Colors.grey[100],
),
backgroundColor: Colors.grey[500],
),
),
SizedBox(height: 10),
Container(
width: MediaQuery.of(context).size.width * 0.5,
margin: EdgeInsets.symmetric(horizontal: 15),
child: FormBuilderTextField(
maxLength: 20,
controller: _inputContactNameController,
textAlign: TextAlign.start,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.words,
attribute: 'contact_person',
decoration: InputDecoration(
prefixIcon: Icon(
Icons.person_outline,
size: 22,
)),
onChanged: (val) {
setState(() {
_customer.name = val;
_formKey
.currentState.fields['contact_person'].currentState
.validate();
});
},
autovalidateMode: AutovalidateMode.always,
validators: [
FormBuilderValidators.required(),
FormBuilderValidators.maxLength(20),
FormBuilderValidators.minLength(2),
],
),
),
Container(
width: MediaQuery.of(context).size.width * 0.5,
margin: EdgeInsets.symmetric(horizontal: 15),
child: FormBuilderTextField(
attribute: 'phone_number',
controller: _inputContactPhoneController,
textAlign: TextAlign.start,
keyboardType: TextInputType.number,
decoration: InputDecoration(
prefixIcon: Icon(
Icons.phone_android,
size: 22,
)),
onChanged: (val) {
setState(() {
_customer.mobile = val;
_formKey.currentState.fields['phone_number'].currentState
.validate();
});
},
validators: [
FormBuilderValidators.required(),
FormBuilderValidators.numeric(),
],
valueTransformer: (text) {
return text == null ? null : num.tryParse(text);
},
),
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
RaisedButton(
color: Colors.white,
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
}),
RaisedButton(
color: Colors.grey[400],
child: Text(
'Save',
style: TextStyle(color: Colors.white),
),
onPressed: () async {
try {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
if (_customer.id == null)
await _dbHelper.insertBikeContact(_customer);
else
await _dbHelper.updateCustomer(_customer);
_refreshBikeSellerList();
_formKey.currentState.reset();
_inputContactNameController.clear();
_inputContactPhoneController.clear();
Navigator.of(context).pop();
}
} catch (e) {
print(e);
}
},
)
],
),
],
),
),
),
);
}
_refreshBikeSellerList() async {
List<Customer> x = await _dbHelper.getCustomer();
setState(() {
_customerList = x;
});
}
}
有什么方法可以在我点击时更新 _mobileController 吗?
如有任何帮助,我们将不胜感激。 提前谢谢你。
已编辑:
class 我保存客户数据的地方:
class Customer {
int id;
String name;
String mobile;
static const tblCustomer = 'Customer';
static const colId = 'id';
static const colName = 'name';
static const colMobile = 'mobile';
Customer({
this.id,
this.name,
this.mobile,
});
Map<String, dynamic> toMap() {
var map = <String, dynamic>{colName: name, colMobile: mobile};
if (id != null) map[colId] = id;
return map;
}
Customer.fromMap(Map<String, dynamic> map) {
id = map[colId];
name = map[colName];
mobile = map[colMobile];
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Customer &&
runtimeType == other.runtimeType &&
name == other.name;
@override
int get hashCode => name.hashCode;
@override
String toString() {
return name;
}
}
这是我的数据库:
import 'dart:async';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'customer.dart';
class DatabaseHelper {
static const _databaseVersion = 1;
static const _databaseName = 'Kiloin.db';
DatabaseHelper._();
static final DatabaseHelper instance = DatabaseHelper._();
Database _database;
Future<Database> get database async {
if (_database != null) return _database;
_database = await _initDatabase();
return _database;
}
_initDatabase() async {
Directory dataDirectory = await getApplicationDocumentsDirectory();
String dbPath = join(dataDirectory.path, _databaseName);
return await openDatabase(
dbPath,
version: _databaseVersion,
onCreate: _onCreateDB,
);
}
_onCreateDB(Database db, int version) async {
await db.execute('''
CREATE TABLE ${Customer.tblCustomer}(
${Customer.colId} INTEGER PRIMARY KEY AUTOINCREMENT,
${Customer.colName} TEXT NOT NULL,
${Customer.colMobile} TEXT NOT NULL
)
''');
}
Future<int> insertBikeContact(Customer customer) async {
Database db = await database;
return await db.insert(Customer.tblCustomer, customer.toMap());
}
Future<List<Customer>> getCustomer() async {
Database db = await database;
List<Map> contact = await db.query(Customer.tblCustomer);
return contact.length == 0
? []
: contact.map((e) => Customer.fromMap(e)).toList();
}
Future<int> updateCustomer(Customer customer) async {
Database db = await database;
return await db.update(Customer.tblCustomer, customer.toMap(),
where: '${Customer.colId}=?', whereArgs: [customer.id]);
}
Future<int> deleteContact(int id) async {
Database db = await database;
return await db.delete(Customer.tblCustomer,
where: '${Customer.colId}=?', whereArgs: [id]);
}
}
您从onSuggestionSelected
获得的价值就是客户。使用该值更新 _mobileController.text
.
onSuggestionSelected: (customer) {
if (customer != null) {
setState(() {
_mobileController.text = customer.mobile;
});
}
}