I am trying to implement an UI as below.
User types his name in the TextField widget and press the button. His name will show as a text widget below the button. I know the StatefulWidget should be used to rebuild the text widget for showing the user input name. However, I am not sure what widgets should be included in the StatefulWidget for the best practice. I tried two different implementations. The first one puts the button and the text widget into the StatefulWidget. The code is
import 'package:flutter/material.dart';
void main() => runApp(const App());
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
var appTitle = const Text('Flutter App');
var appBody = const AppBody2();
var appBar = AppBar(
title: appTitle,
);
var app = MaterialApp(
home: Scaffold(
appBar: appBar,
body: appBody,
),
);
return app;
}
}
final nameController = TextEditingController();
class AppBody2 extends StatelessWidget {
const AppBody2({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final nameField = TextField(
controller: nameController,
style: const TextStyle(fontSize: 20),
decoration: const InputDecoration(
labelText: 'Your name',
labelStyle: TextStyle(fontSize: 20),
),
);
final nameWidget = _NameButtonAndText();
final widget = Center(
child: Column(
children: <Widget>[
Container(child: nameField, width: 200, margin: const EdgeInsets.symmetric(vertical: 10),),
Container(child: nameWidget, margin: const EdgeInsets.symmetric(vertical: 10),),
],
),
);
return widget;
}
}
class _NameButtonAndText extends StatefulWidget {
@override
State<StatefulWidget> createState() => _NameButtonAndTextState();
}
class _NameButtonAndTextState extends State<_NameButtonAndText> {
@override
Widget build(BuildContext context) {
final btn = ElevatedButton(
child: const Text('OK'),
onPressed: () => setState(() {}),
);
final widget = Center(
child: Column(
children: <Widget>[
Container(child: btn, margin: const EdgeInsets.symmetric(vertical: 10),),
Container(child: Text(nameController.text, style: const TextStyle(fontSize: 20)),),
],
),
);
return widget;
}
}
I think there are two bad things in this implementation. Firstly, the TextEditingController is a global object and, secondly, the button and the text widget are recreated when the user presses the button. The recreation of the button should be unnecessary because it doesn't change. So I tried the 2nd implementation.
import 'package:flutter/material.dart';
void main() => runApp(const App());
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
var appTitle = const Text('Flutter App');
var appBody = const AppBody();
var appBar = AppBar(
title: appTitle,
);
var app = MaterialApp(
home: Scaffold(
appBar: appBar,
body: appBody,
),
);
return app;
}
}
class AppBody extends StatelessWidget {
const AppBody({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final nameController = TextEditingController();
final nameField = TextField(
controller: nameController,
style: const TextStyle(fontSize: 20),
decoration: const InputDecoration(
labelText: 'Your name',
labelStyle: TextStyle(fontSize: 20),
),
);
final nameWidget = _NameWidget(GlobalKey<_NameWidgetState>());
final btn = ElevatedButton(
child: Text('OK'),
onPressed: () => nameWidget.setName(nameController.text),
);
final widget = Center(
child: Column(
children: <Widget>[
Container(child: nameField, width: 200, margin: const EdgeInsets.symmetric(vertical: 10),),
Container(child: btn, margin: const EdgeInsets.symmetric(vertical: 10),),
Container(child: nameWidget, margin: const EdgeInsets.symmetric(vertical: 10),),
],
),
);
return widget;
}
}
class _NameWidget extends StatefulWidget {
final GlobalKey<_NameWidgetState> _key;
_NameWidget(this._key): super (key: _key);
@override
State<StatefulWidget> createState() => _NameWidgetState();
setName(String name) {
_key.currentState?.setName(name);
}
}
class _NameWidgetState extends State<_NameWidget> {
String _name = '';
@override
Widget build(BuildContext context) {
final widget = Text(_name,
style: const TextStyle(fontSize: 20));
return widget;
}
setName(String name) {
setState(() {
_name = name;
});
}
}
I wrapped only the text widget into the StatefulWidget. When user inputs his name and presses the button. Only the text widget is rebuilded to show the input name. However, the 2nd implementation used a GlobalKey, which seems not good for efficiency considerations. I wonder which one is better between these two implementations or there is some other best practice for this case. Thanks for your reading and comments.