[英]How to call a function in a Stateful Widget from another StatefulWidget?
[英]Call function from Stateful Widget from another StatefulWidget
因此,基本上我有一个ProfilePage
,它是一个StatelessWidget
,在它的构建方法内部,我显示了一个名为MyForm
的表单(它是一个StatefulWidget
和一个名为FancyFab
的窗口小部件(这是另一个StatefulWidget
。
这是一个如何在父窗口小部件上显示它们的示例:
@override
Widget build(BuildContext globalContext) {
return Scaffold(
appBar: AppBar(title: Text('Profile')),
floatingActionButton: FancyFab(),
body: Center(
child: FutureBuilder(
future: initData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return MyForm(data: snapshot.data);
} else if (snapshot.hasError) {
print(snapshot.error);
return new Container(width: 0.0, height: 0.0);
} else {
return Center(child: CircularProgressIndicator());
}
},
)
)
);
}
我的问题在于,我在MyFormState
有一个saveData()
函数,该函数从每个TextFormField
控制器获取值并将其保存在数据库中。 我需要从我的FancyFab
小部件中调用此函数,但找不到合适的方法。 甚至从我的FancyFab
小部件中访问那些TextFormField
控制器。 任何帮助或指导将不胜感激。
编辑
这是我实现FancyFab
小部件的方式:
class FancyFab extends StatefulWidget {
final String tooltip;
final IconData icon;
String photo;
TextEditingController birthController;
TextEditingController firstController;
TextEditingController lastController;
TextEditingController emailController;
TextEditingController phoneController;
TextEditingController associationController;
TextEditingController countryController;
final Function saveData;
FancyFab({
this.tooltip,
this.icon,
this.saveData,
this.firstController,
this.lastController,
this.emailController,
this.phoneController,
this.associationController,
this.countryController,
this.birthController,
this.photo});
@override
_FancyFabState createState() => _FancyFabState();
}
class _FancyFabState extends State<FancyFab>
with SingleTickerProviderStateMixin {
bool isOpened = false;
AnimationController _animationController;
Animation<Color> _buttonColor;
Animation<double> _animateIcon;
Animation<double> _translateButton;
Curve _curve = Curves.easeOut;
double _fabHeight = 56.0;
@override
initState() {
_animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 500))
..addListener(() {
setState(() {});
});
_animateIcon =
Tween<double>(begin: 0.0, end: 1.0).animate(_animationController);
_buttonColor = ColorTween(
begin: Colors.pink,
end: Colors.red,
).animate(CurvedAnimation(
parent: _animationController,
curve: Interval(
0.00,
1.00,
curve: Curves.linear,
),
));
_translateButton = Tween<double>(
begin: _fabHeight,
end: -14.0,
).animate(CurvedAnimation(
parent: _animationController,
curve: Interval(
0.0,
0.75,
curve: _curve,
),
));
super.initState();
}
@override
dispose() {
_animationController.dispose();
super.dispose();
}
animate() {
if (!isOpened) {
_animationController.forward();
} else {
_animationController.reverse();
}
isOpened = !isOpened;
}
Widget save() {
return Container(
child: FloatingActionButton(
heroTag: 'saveBtn',
onPressed: () {
},
tooltip: 'Save',
child: Icon(Icons.save),
),
);
}
Widget image() {
return Container(
child: FloatingActionButton(
heroTag: 'imageBtn',
onPressed: () async {
File file = await FilePicker.getFile(type: FileType.IMAGE);
AlertDialog alert = AlertDialog(
title: Text('Uploading photo'),
titlePadding: EdgeInsets.all(20.0),
contentPadding: EdgeInsets.all(20.0),
elevation:10,
content: CircularProgressIndicator(),
);
final StorageReference storageRef = FirebaseStorage.instance.ref().child(file.path);
final String fileName = file.path;
final StorageUploadTask uploadTask = storageRef.putFile(
File(fileName),
);
final StorageTaskSnapshot downloadUrl =
(await uploadTask.onComplete);
final String url = (await downloadUrl.ref.getDownloadURL());
print('URL Is $url');
setState(() {
widget.photo = url;
});
},
tooltip: 'Image',
child: Icon(Icons.image),
),
);
}
Widget toggle() {
return Container(
child: FloatingActionButton(
backgroundColor: _buttonColor.value,
onPressed: animate,
heroTag: 'toggleBtn',
tooltip: 'Toggle',
child: AnimatedIcon(
icon: AnimatedIcons.menu_close,
progress: _animateIcon,
),
),
);
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Transform(
transform: Matrix4.translationValues(
0.0,
_translateButton.value * 2.0,
0.0,
),
child: save(),
),
Transform(
transform: Matrix4.translationValues(
0.0,
_translateButton.value * 1.0,
0.0,
),
child: image(),
),
toggle(),
],
);
}
}
据我所知,应用程序的数据流有一个小问题,例如,严格地基于基于功能性的观察,数据和触发器(FancyFab)应该在同一小部件中或可直接访问小部件。 但是,如果您想继续, 这是一种非常原始的方法。
我设法通过在MyForm
小部件中嵌套一个Scaffold
来解决此问题。 然后树变了,我的FancyFab
小部件现在是MyForm
小部件的子级。 接下来,我将父级的saveData()
函数和所有控制器传递给了子窗口小部件,这样我就可以调用提供适当数据的父级函数。 解决方法如下:
class MyFormState extends State<MyForm> {
User user;
TextEditingController birthController = TextEditingController();
TextEditingController firstController = TextEditingController();
TextEditingController lastController = TextEditingController();
TextEditingController emailController = TextEditingController();
TextEditingController phoneController = TextEditingController();
TextEditingController associationController = TextEditingController();
TextEditingController countryController = TextEditingController();
@override
void initState() {
super.initState();
user = widget.data[1];
firstController.text = user.firstName;
lastController.text = user.lastName;
countryController.text = user.country;
birthController.text = user.dateBirth;
emailController.text = user.email;
phoneController.text = user.phone;
associationController.text = user.association;
}
_updatePhoto(String text) {
setState(() {
user.photo = text;
});
}
_saveData(data, BuildContext ctx) async {
FirebaseUser fireUser= await FirebaseAuth.instance.currentUser();
var uid = fireUser.uid;
await Firestore.instance.collection('users').document(uid).updateData(data).then((val) {
AlertDialog alert = AlertDialog(
title: Text('Confirmation'),
titlePadding: EdgeInsets.all(20.0),
// contentPadding: EdgeInsets.all(20.0),
elevation:10,
content: Text('Your profile has been saved.')
);
showDialog(context: ctx,
builder: (BuildContext context){
return alert;
});
})
.catchError((err) {
final snackBar = SnackBar(
content: Text('There have been an error updating your profile' + err.toString()),
elevation: 10,
);
Scaffold.of(ctx).showSnackBar(snackBar);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FancyFab(
parentAction: _updatePhoto,
saveData: _saveData,
firstController: firstController,
lastController: lastController,
emailController: emailController,
countryController: countryController,
associationController: associationController,
phoneController: phoneController,
birthController: birthController,
photo: user.photo),
body:Form(
//more UI
)
);
}
}
class FancyFab extends StatefulWidget {
final void Function(String value) parentAction;
final void Function(dynamic data, BuildContext ctx) saveData;
final String tooltip;
final IconData icon;
String photo;
TextEditingController birthController;
TextEditingController firstController;
TextEditingController lastController;
TextEditingController emailController;
TextEditingController phoneController;
TextEditingController associationController;
TextEditingController countryController;
FancyFab({
this.parentAction,
this.tooltip,
this.icon,
this.saveData,
this.firstController,
this.lastController,
this.emailController,
this.phoneController,
this.associationController,
this.countryController,
this.birthController,
this.photo});
@override
_FancyFabState createState() => _FancyFabState();
}
然后,我可以在子窗口小部件状态上使用widget.parentFunction(data)
调用父函数。
特别感谢@diegoveloper提供了一篇很棒的文章,解释了小部件交互的类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.