I want to add textfield in a text widget.
like that:
I want to ___ textfield in a text widget.
You can use two seperate Text widgets and a TextField widget in between, all in a Row widget. like this:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: Center(
child: MyStatefulWidget(),
),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
#override
_State createState() => _State();
}
class _State extends State<MyStatefulWidget> {
bool isTextFieldEnabled = true;
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: [
Text('I want to'),
Container(
width: 200,
child: TextField(
enabled: isTextFieldEnabled,
),
),
Text('textfield in a text widget.'),
],
));
}
}
Related
Here Is the main.dart file:
import 'package:flutter/material.dart';
import 'package:untitled/button.dart';
import 'package:untitled/form.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[MyForm(), MyButton()],
),
),
);
}
}
Here is form.dart file:
import 'package:flutter/material.dart';
class MyForm extends StatelessWidget {
#override
Widget build(BuildContext context) {
TextEditingController x = TextEditingController();
return Container(
child: Center(
child: TextField(
controller: x,
decoration: InputDecoration(labelText: "Name"),
),
),
);
}
}
Here is button.dart file:
import 'package:flutter/material.dart';
class MyButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: ElevatedButton(
onPressed: () {
//if I press this button the textediting field will show 10
},
child: Text("Pass 10"),
),
),
);
}
}
How can I show data in text editingfield if I press the button in button.dart?
What is the optimal way to do these things?
Should I use statefulwidgets in statelesswidget or statelesswidgets in statefulwidgets?
My thought is to use redux or provider to pass this kind of things. Although I don't know this simple thing need state management system or not.
You need to lift the state up (create your controller in the main and pass it to the children)
// main.dart
class _MyHomePageState extends State<MyHomePage> {
TextEditingController x = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[MyForm(x), MyButton(x)],
),
),
);
}
}
class MyForm extends StatelessWidget {
MyForm(this.x);
final TextEditingController x;
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: TextField(
controller: x,
decoration: InputDecoration(labelText: "Name"),
),
),
);
}
}
class MyButton extends StatelessWidget {
MyButton(this.x);
final TextEditingController x;
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: ElevatedButton(
onPressed: () {
x...
//if I press this button the textediting field will show 10
},
child: Text("Pass 10"),
),
),
);
}
}
Even after reading this and this, I still can't seem to wrap my head around storing page states in Flutter.
I've built a sample app, which has a main page called MyHomePage and a second page called SecondPage. MyHomePage has a floating action button, which displays SecondPage via Navigator.push(...). The second page contains a text field with an assigned controller. I would like to preserve the text field's text after I close and reopen SecondPage.
I've tried all sorts of combinations with setting buckets, page states and keys (inspired by the links above), but I couldn't make it work.
Also I'd like to store the whole page state automatically - without the need to write/retrieve every single value manually (in case I have a lot of text fields on the page).
Here is my code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
PageStorageKey mykey = new PageStorageKey("testkey");
class MyApp extends StatelessWidget {
final PageStorageBucket _bucket = new PageStorageBucket();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: PageStorage(
bucket: _bucket,
child: MyHomePage(),
)
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("State demo"),
),
body: Center(
child: Column(
children: <Widget>[
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _openSecondPage,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
_openSecondPage() {
Navigator.push(context, new MaterialPageRoute(builder: (context) => new SecondPage()));
}
}
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
final _aController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second page"),
),
body: Center(
child: TextField(
controller: _aController,
key: mykey,
autofocus: true,
),
)
);
}
}
EDIT:
Based on Ajay's answer, I was able to greatly simplify the working code. Turns out that in order to persist widget states manually, all you need is an instance of PageStorageBucket in combination with ValueKey instances.
Here are the modifications I did to Ajay's code:
Removed the after_layout plugin (initState method is sufficient).
Removed the global PageStorageKey instance (replaced it with a local ValueKey instance in the page that needs to use it).
Removed global instance of PageStorageBucket and replaced it with a final instance in MyApp, which is passed to the pages that need it via constructor attributes.
Removed PageStorage from the component tree.
Here is the resulting code (simplest working form):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final bucket = PageStorageBucket();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(bucket: bucket,),
);
}
}
class MyHomePage extends StatefulWidget {
final PageStorageBucket bucket;
const MyHomePage({Key key, this.bucket}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("State demo"),
),
body: Center(
child: Column(
children: <Widget>[],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _openSecondPage,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
_openSecondPage() {
Navigator.push(
context, new MaterialPageRoute(builder: (context) => new SecondPage(bucket: widget.bucket,)));
}
}
class SecondPage extends StatefulWidget {
final PageStorageBucket bucket;
const SecondPage({Key key, this.bucket}) : super(key: key);
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
static const KEY_A = ValueKey("secondPage.A");
final _aController = TextEditingController();
#override
void initState() {
super.initState();
_aController.addListener(_updateValue);
String value = widget.bucket.readState(context, identifier: KEY_A) ?? "";
_aController.text = value;
}
_updateValue() {
widget.bucket.writeState(context, _aController.text, identifier: KEY_A);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second page"),
),
body: Center(
child: TextField(
controller: _aController,
autofocus: true,
),
),
);
}
}
you need to read and write the state as well.
Check out the below code.
Note: I have used after_layout to initialize the text controller.
import 'package:after_layout/after_layout.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
PageStorageKey mykey = new PageStorageKey("testkey");
final PageStorageBucket _bucket = new PageStorageBucket();
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: PageStorage(
bucket: _bucket,
child: MyHomePage(),
));
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("State demo"),
),
body: Center(
child: Column(
children: <Widget>[],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _openSecondPage,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
_openSecondPage() {
Navigator.push(
context, new MaterialPageRoute(builder: (context) => new SecondPage()));
}
}
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage>
with AfterLayoutMixin<SecondPage> {
final _aController = TextEditingController();
#override
void initState() {
super.initState();
_aController.addListener(_updateValue);
}
#override
void afterFirstLayout(BuildContext context) {
String value =
_bucket.readState(context, identifier: ValueKey(mykey)) ?? "";
print(value);
_aController.text = value;
}
_updateValue() {
_bucket.writeState(context, _aController.text, identifier: ValueKey(mykey));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second page"),
),
body: Center(
child: TextField(
controller: _aController,
key: mykey,
autofocus: true,
),
),
);
}
}
I am working on a flutter app, and I have some data stored in the state of a widget. In this case it is the string title. I am wondering if I can pass this data through a parent stateless widget and into this stateless widgets parent, which is a stateful widget. If working correctly, I could pass title into the state of MyHomePage and save it into title2. Is there a way to do this or do I have to convert Widget1 into a stateful widget. The only issue with that is that I already wrote the widget, but I am curious. Here is my code. Thanks!
//main.dart
import 'package:flutter/material.dart';
import 'Widget1.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String title2;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Hello"),
),
body: Center(
child: Widget1(),
),
);
}
}
/////////////////////////////////////////////
//Widget1.dart
Widget Widget1() {
return Widget2();
}
/////////////////////////////////
//Widget2.dart
import 'package:flutter/material.dart';
class Widget2 extends StatefulWidget {
final String title = "Hello from Widget2";
_Widget2State createState() => _Widget2State();
}
class _Widget2State extends State<Widget2> {
String title = "Hello from Widget2";
#override
Widget build(BuildContext context) {
return Text('${title}');
}
}
Thanks again!
If you are not using any state management except default one then you can pass data between widgets using Navigator. Here is the code example of how to pass String from child Stateless widget (can be stateful too) to its parent widget.
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String title = "";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("FIRST WIDGET"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Title from child Stateless widget: $title"),
FlatButton(
onPressed: () => _openSecondWidget(),
child: Text("OPEN SECOND WIDGET"),
)
],
),
),
);
}
void _openSecondWidget() async {
var newTitle = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SecondWidget(),
),
);
setState(() {
title = newTitle;
});
}
}
class SecondWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("SECOND WIDGET"),
),
body: Center(
child: FlatButton(
onPressed: () {
Navigator.of(context).pop("Hi from Second Widget");
},
child: Text("GO BACK"),
),
),
);
}
}
So the button on the first widget is pushing new widget on the screen and awaits for its result. When it gets the result from the second widget I'm using setState updating display of the title variable. And second widget has just one button which removes this widget from the back stack with some parameter which is in this case String, but it can be anything else.
I assume you just want to pass data from StatelessWidget to StatefulWidget and want to access it in its State. Then try this,
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: "Flutter Demo",),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({Key key, this.title}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title), //TODO: use `widget` to access properties
),
body: Center(
child: MyWidget(title: widget.title,),
),
);
}
}
class MyWidget extends StatefulWidget {
final String title;
const MyWidget({Key key, this.title}) : super(key: key);
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
#override
Widget build(BuildContext context) {
return Text('${widget.title}');
}
}
My application has scaffold.
But I want to change only the body of scaffold.
Normally I use setState() to change the state, but in this case, How can I use setState() or I can do some other way??
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
void onTapped() {
print ("tapped");
// I want to change only body of Scaffold like this
// body: new Text("new body");
};
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body:
GestureDetector(
onTap: () => onTapped(),
child:Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
],
),
),
),
);
}
}
If you want to just setState within the body of the scaffold, make the body a stateful widget and call setState within that widget. You can define your own stateless and stateful widgets by extending StatelessWidget or StatefulWidget. It is useful to define a particular thing as its own widget instead of just as a method that returns a widget because of how Flutter compartmentalizes the rebuilding process. If the body of the scaffold is its own widget, only that widget will be rebuilt when you call setState. If you do what the other answer suggests, you will rebuild MyHomePage, which includes the scaffold. On the other hand, if you define a stateful widget with a smaller scope, and then call setState() within that widget, only the widget with the smaller scope will be rebuilt.
For example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
Widget build(BuildContext context) {
print('scaffold rebuilt');
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: ScaffoldBody(),
);
}
}
class ScaffoldBody extends StatefulWidget {
#override
_ScaffoldBodyState createState() => _ScaffoldBodyState();
}
class _ScaffoldBodyState extends State<ScaffoldBody> {
int timesTapped = 0;
void onTapped() {
setState(() {
timesTapped++;
});
}
#override
Widget build(BuildContext context) {
print('scaffold body rebuilt');
return GestureDetector(
onTap: onTapped,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times: $timesTapped',
),
],
),
),
);
}
}
You can create a variable Widget _scaffoldBody; to hold the current Scaffold body.
You set an initial value to it, and then call setState when you need to change the body.
Something like this:
class _MyHomePageState extends State<MyHomePage> {
Widget _scaffoldBody;
#override
void initState(){
// Initialize it with the first body you want visible.
_scaffoldBody = GestureDetector(
onTap: () => onTapped(),
child:Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
],
),
),
);
}
// Note: move the onTapped method inside the state so you can call setState;
void onTapped()
// Call setState changing the body
setState((){
_scaffoldBody = Text("new body");
});
};
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: _scaffoldBody,
);
}
}
I've create simple PageView app to test multiple pages.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
final firstPage = FirstPage(key: Key("FirstPage"));
final secondPage = SecondPage(key: Key("SecondPage"));
debugPrint("_MyHomePageState.build");
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: PageView(
children: <Widget>[
firstPage,
secondPage,
],
),
);
}
}
class FirstPage extends StatelessWidget {
FirstPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
debugPrint("FirstPage.build");
return Container(
child: Center(
child: Text("First Page"),
),
);
}
}
class SecondPage extends StatelessWidget {
SecondPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
debugPrint("SecondPage.build");
return Container(
child: Center(
child: Text("Second Page"),
),
);
}
}
Even thought _MyHomePageState.build has been shown only once, FirstPage.build and SecondPage.build were printed on every page changes.
What I'd like to prevent unnecessary page draw, how can I accomplish this?
You can achieve so by using
1. const keyword
Make your widgets accept to be const:
class FirstPage extends StatelessWidget {
const FirstPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
debugPrint("FirstPage.build");
return Container(
child: Center(
child: Text("First Page"),
),
);
}
}
and call it with const keyword:
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: PageView(
children: <Widget>[
const firstPage(),
const secondPage(),
],
),
);
2. AutomaticKeepAliveClientMixin
Convert your StatelessWidget to StatefullWidget.
class FirstPage extends StatefulWidget {
FirstPage({Key key}) : super(key: key);
#override
_FirstPageState createState() => _FirstPageState();
}
class _FirstPageState extends State<FirstPage> {
#override
Widget build(BuildContext context) {
debugPrint("FirstPage.build");
return Container(
child: Center(
child: Text("First Page"),
),
);
}
}
Extends AutomaticKeepAliveClientMixin on StatefullWidget created State.
class _FirstPageState extends State<FirstPage> with AutomaticKeepAliveClientMixin {
Call super on the build method.
#override
Widget build(BuildContext context) {
super.build(context);
debugPrint("FirstPage.build");
return Container(
child: Center(
child: Text("First Page"),
),
);
}
Override wantKeepAlive getter with true returned value.
#override
bool get wantKeepAlive => true;
And then your widget tree won't dispose of this widget so it won't rebuild over and over.
Code Example:
class FirstPage extends StatefulWidget {
FirstPage({Key key}) : super(key: key);
#override
_FirstPageState createState() => _FirstPageState();
}
class _FirstPageState extends State<FirstPage>
with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
super.build(context);
debugPrint("FirstPage.build");
return Container(
child: Center(
child: Text("First Page"),
),
);
}
#override
bool get wantKeepAlive => true;
}
3. MVVM Architecture with any State-management solution you like
It will save your state on ViewModel away from the View, so your UI can rebuild itself anytime it wants with no worries about your State because the ViewModel is still the same.
You should always imagine that your build() methods (for both StatefulWidget and StatelessWidget) are being called 60 times per second, so they should be simple and idempotent. Anything else should be moved into a StatefulWidget initState() and friends.
It's easy!
pageController can help you.
Just in your _MyHomePageState
Declare final pageController = PageController(keepPage: false);
And in your PageView
PageView(
controller: pageController,
children: <Widget>[
firstPage,
secondPage,
],
)
Good Luck.