I have an app with a page that has a ListView.builder().
Each row of the list is a stateful widget (match_chat_row.dart).
When you click on that stateful widget, it's navigating to another stateful widget (chat_screen.dart).
chat_screen.dart has a TextField.
The problem is that last few widgets(match_chat_row.dart) of the ListView are being disposed when the keyboard pops up on one of the first match_chat_row's chat_screen.dart.
I made this simple app to demonstrate it:
main.dart
import 'package:flutter/material.dart';
import 'match_chat_row.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#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> {
final String _pageName = "Screen";
List<String> _chats = ["A","B","C","D","E","F","G","H","I","J"];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: _body()
);
}
_body(){
return ListView.builder(
itemCount: _chats.length,
itemBuilder: (_, index) => _buildRow(_chats[index]),
);
}
Widget _buildRow(String chat) {
return
Column(
key: ValueKey<String>('chat-$chat'),
children: [
Center(
child:
MatchChatRow(
content: chat,
),
),
Divider(color: Colors.grey.withOpacity(0.20), thickness: 0.8, height: 0.0,)
],
);
}
}
match_chat_row.dart
import 'package:flutter/material.dart';
import 'chat_screen.dart';
class MatchChatRow extends StatefulWidget {
final String content;
const MatchChatRow({required this.content});
#override
_MatchChatRowState createState() => _MatchChatRowState();
}
class _MatchChatRowState extends State<MatchChatRow> {
#override
void dispose(){
print("Disposed ROW! ${widget.content}");
super.dispose();
}
#override
Widget build(BuildContext context) {
final rowHeight = 100.0;
final rowPadding = 10.0;
return
InkWell(
onTap: () =>
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatScreen(
key: ValueKey(widget.content),
),
),
),
child:
Container(
height: rowHeight,
padding: EdgeInsets.all(rowPadding),
child: Row(
children: <Widget>[
SizedBox(
width: 20.0,
),
Expanded(
child:
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(widget.content),
Text("_buildSubtitle()"),
],
)
),
],
),
)
);
}
}
chat_screen.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class ChatScreen extends StatefulWidget {
const ChatScreen({required Key key,}): super(key: key);
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {
final TextEditingController _messageController = TextEditingController();
bool _isComposingMessage = false;
bool _isChatExist = false;
bool _isLoading = false;
bool _isSending = false;
#override
void initState() {
super.initState();
}
#override
void dispose(){
print("disposed chat! ${widget.key}");
super.dispose();
}
Container _buildMessageTF() {
return Container(
margin: const EdgeInsets.only(left: 8, right: 8, bottom: 8),
decoration: BoxDecoration(
border:
Border.all(color: Theme.of(context).accentColor.withOpacity(0.3)),
borderRadius: BorderRadius.circular(30)),
child: Row(
children: <Widget>[
Expanded(
child:
TextField(
minLines: 1,
maxLines: 4,
controller: _messageController,
textCapitalization: TextCapitalization.sentences,
onChanged: (messageText) {
setState(() => _isComposingMessage = messageText.isNotEmpty);
},
decoration: InputDecoration(
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
hintText: 'Message...'),
),
),
],
),
);
}
#override
Widget build(BuildContext context) {
return
WillPopScope(
onWillPop: () {
return Future.value(true);
},
child:
SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text("Hi")
),
body:
SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
if (!_isChatExist && !_isLoading) SizedBox.shrink(),
_buildMessageTF(),
],
),
),
),
)
);
}
}
And whenever I click on the TextField and the Keyboard pops up the below widgets are being disposed (this is my debug console):
I/flutter (23372): Disposed ROW! I
I/flutter (23372): Disposed ROW! J
I/flutter (23372): Disposed ROW! H
update your _body from main
_body(){
return SingleChildScrollView(
child: ListView.builder(
shrinkWrap: true,
physics: ScrollPhysics(),
itemCount: _chats.length,
itemBuilder: (_, index) => _buildRow(_chats[index]),
),
);
}
Related
While I am typing the message it encodes it into white but when I have sent it, it turns to black which is the same with the response that I have been receiving. Kindly help me how will i change it to white.
import 'package:dialog_flowtter/dialog_flowtter.dart';
import 'package:flutter/material.dart';
import 'Messages.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'QuickTropa',
theme: ThemeData(
primaryColor: Color(0XFFf7c59f),
),
home: Home(),
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
late DialogFlowtter dialogFlowtter;
final TextEditingController _controller = TextEditingController();
List<Map<String, dynamic>> messages = [];
#override
void initState() {
DialogFlowtter.fromFile().then((instance) => dialogFlowtter = instance);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('QuickTropa'),
backgroundColor: Color(0XFF087830),
),
body: Container(
child: Column(
children: [
Expanded(child:
MessagesScreen(
messages:
messages)),
Container(
padding: EdgeInsets.symmetric(
horizontal: 14,
vertical: 8
),
color: Color(0XFF087830),
child: Row(
children: [
Expanded(child:
TextField(
controller: _controller,
style: TextStyle(color: Colors.white),
)),
IconButton(
onPressed: () {
sendMessage(_controller.text);
_controller.clear();
},
color: Colors.white ,
icon: Icon(Icons.send))
],
),
)
],
),
),
);
}
sendMessage(String text) async {
if (text.isEmpty) {
print('Message is empty');
} else {
setState(() {
addMessage(Message(
text: DialogText(text: [text])), true);
});
DetectIntentResponse response = await dialogFlowtter.detectIntent(
queryInput: QueryInput(text: TextInput(text: text)));
if (response.message == null) return;
setState(() {
addMessage(response.message!);
});
}
}
addMessage(Message message, [bool isUserMessage = false]) {
messages.add({
'message': message,
'isUserMessage': isUserMessage
});
}
}
import 'package:dialog_flowtter/dialog_flowtter.dart';
import 'package:flutter/material.dart';
class MessagesScreen extends StatefulWidget {
final List messages;
const MessagesScreen({Key? key, required this.messages}) : super(key: key);
#override
State<MessagesScreen> createState() => _MessagesScreenState();
}
class _MessagesState extends State<MessagesScreen> {
#override
Widget build(BuildContext context) {
return const Placeholder();
}
}
class _MessagesScreenState extends State<MessagesScreen> {
#override
Widget build(BuildContext context) {
var w = MediaQuery.of(context).size.width;
return ListView.separated(
itemBuilder: (context, index) {
return Container(
margin: EdgeInsets.all(10),
child: Row(
mainAxisAlignment: widget.messages[index]['isUserMessage']
? MainAxisAlignment.end
: MainAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.symmetric(vertical: 14, horizontal: 14),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(
20,
),
topRight: Radius.circular(20),
bottomRight: Radius.circular(
widget.messages[index]['isUserMessage'] ? 0 : 20),
topLeft: Radius.circular(
widget.messages[index]['isUserMessage'] ? 20 : 0),
),
color: widget.messages[index]['isUserMessage']
? Color(0XFF087830)
: Color(0XFF087830).withOpacity(0.8)),
constraints: BoxConstraints(maxWidth: w * 2 / 3),
child:
Text(widget.messages[index]['message'].text.text[0])),
],
),
);
},
separatorBuilder: (_, i) => Padding(padding: EdgeInsets.only(top: 10)),
itemCount: widget.messages.length);
}
}
I tried putting Color:Colors.white isnide the box decoration but of course it wouldn't let me. I can't think anymore where to put it.
Try this
Text(widget.messages[index]['message'].text.text[0],
style:TextStyle(color:Colors.white,)
)
on swipe how can I set text in the text field after that edit text and insert it again in that position. swipe in Dismissible and after swipe, I want my text on the input text field and change the text and add the text in the same position can you please help me strong text
enter image description here
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import '../models/model.dart';
import 'list_provider.dart';
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late GlobalKey<FormState> _formKey;
late TextEditingController _controller;
var taskItems;
int counter = 0;
late DynamicList listClass;
bool isUpdate =false;
#override
void initState() {[enter image description here][1]
// TODO: implement initState
super.initState();
_formKey = GlobalKey();
_controller = TextEditingController();
taskItems = Provider.of<ListProvider>(context, listen: false);
listClass = DynamicList(taskItems.list);
}
#override
void dispose() {
// TODO: implement dispose
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: TextFormField(
controller: _controller,
onSaved: (val) {
taskItems.addItem(val);
},
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState?.save();
_controller.clear();
}
},
child: Text('Add'),
),
),
const Padding(padding: EdgeInsets.all(8.0),
),
Consumer<ListProvider>(builder: (context, provider, listTile) {
return Expanded(
child: ListView.builder(
itemCount: listClass.list.length,
itemBuilder: buildList,
),
);
}),
],
),
**
-
> Heading
**
));
}
Widget buildList(BuildContext context, int index) {
counter++;
return Dismissible(
key: Key(counter.toString()),
direction: DismissDirection.startToEnd,
onDismissed: (direction) {
taskItems.deleteItem(index);
},
child: Container(
margin: EdgeInsets.all(4),
decoration: BoxDecoration(
border: Border.all(
color: Colors.blue,
width: 2,
),
borderRadius: BorderRadius.circular(10)),
child: ListTile(
title: Text(listClass.list[index].toString()),
trailing: IconButton(iconSize: 30, icon: Icon(Icons.delete),
onPressed: () {
setState(() {
taskItems.deleteItem(index);
},
);
},
),
),
),
);
}
}
I have an app which has two tabs. One of the which is the "SAVED ITEMS" tab. When I save the items (from a different screen of ALL ITEMS LIST) it gets saved and on even switching the tabs works fine. But when I close the app and reopen it, the "SAVED ITEMS" list is empty and I've to select the items again. I've used the AutomaticKeepAliveClientMixin but its not helping. Any idea on how to solve this folks?
My code:
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
super.build(context);
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.blue,
accentColor: Colors.white,
),
home: DefaultTabController(
length: 2,
child: Scaffold(
drawer: Drawer(),
backgroundColor: Colors.blueAccent,
appBar: AppBar(
backgroundColor: Colors.blueAccent,
title: Text('AIO'),
bottom: TabBar(
tabs: <Widget>[
Tab(icon: Icon(Icons.search)),
Tab(icon: Icon(Icons.favorite)),
],
),
),
body: TabBarView(
children: <Widget>[
gridView,
SecondPage(),
],
),
),
),
);
}
#override
bool get wantKeepAlive => true;
}
SecondTab code:
Set<int> favorites = {};
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage>
with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
return Stack(
fit: StackFit.expand,
children: <Widget>[
_getFavoriteList(),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: FloatingActionButton(
child: Icon(
Icons.add,
color: Colors.blue,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditFavorites(),
),
).then((updatedFavorites) {
if (updatedFavorites != null)
// setState(() {
favorites = updatedFavorites;
// });
});
},
),
),
)
],
);
}
Add shared_preferences dependency in pubspec.yaml
Update your main()
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences pref = await SharedPreferences.getInstance();
pref.getStringList("favorites")?.forEach((fav){
favorites.add(int.tryParse(fav));
});
runApp(MyApp());
}
then update your SecondPage
Set<int> favorites = {};
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
Future<void> _upDateFavorites(Set<int> updatedFavorites) async {
print("secong: u$updatedFavorites");
SharedPreferences pref = await SharedPreferences.getInstance();
List<String> favoritesAsString =
updatedFavorites.map((fav) => fav.toString()).toList(); //TODO: Change `favorites` to `updatedFavorites`
print(favoritesAsString);
await pref.setStringList("favorites", favoritesAsString); //TODO: await here to store it completely
favorites = updatedFavorites;
setState(() {});
}
#override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: <Widget>[
_getFavoriteList(),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: FloatingActionButton(
child: Icon(
Icons.add,
color: Colors.blue,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditFavorites(),
),
).then((updatedFavorites) async {
if (updatedFavorites != null)
// setState(() {
_upDateFavorites(updatedFavorites);
// });
});
},
),
),
)
],
);
}
Widget _getFavoriteList() {
if (favorites?.isNotEmpty == true)
return _FavoriteList();
else
return _EmptyFavoriteList();
}
}
Don't forget to import 'package:shared_preferences/shared_preferences.dart'; where you use SharedPreferences
Avoid storing widget in a vaiable, if it needs to be rebuild.
So delete var favGridView = GridView.builder( ....
class _FavoriteList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GridView.builder(
itemCount: favorites.length,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemBuilder: (BuildContext context, int index) {
print("kkkkkkkkkkk: ${favorites.elementAt(index)}");
return InkWell(
child: Card(
elevation: 10,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(10),
child: Container(
child: Image.asset(
'lib/images/${images[favorites.elementAt(index)]}'), //TODO: Change this
// child: SizedBox(child: Text('yashjha'),),
// decoration: BoxDecoration(
// image: DecorationImage(
// image: AssetImage('lib/images/${images[index]}'),
// fit: BoxFit.fitWidth,
// alignment: Alignment.topCenter,
// ),
// ),
),
),
Text(nameOfSite[favorites.elementAt(index)]), //TODO: Change This
],
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Text("dsadsa")),
);
},
);
},
);
}
}
I also recommend using actions instead of FloatingButton in _EditFavoritesState. Because the floating button hides last item. which makes it unable to add to favorites
AutomaticKeepAliveClientMixin is used to retain the data when the app is running and you need to retain data while switching between tabs, pages etc.
I would suggest you use SharedPreferences to easily save data to your memory that can be retrieved when the app is started again.
SharedPreferences: https://pub.dev/packages/shared_preferences
Currently, I have write codes for making up the counter app with two buttons. 1 raised button to reset and one fab button for increment counter.
is it possible to add the countdown timer to implement on FAB button? When FAB button clicks 20-second countdown timer start.
Also, I have found below thread for the same type of function implement. But I don't where to put codes in my app to implement countdown work on FAB button.
How to make a countdown in flutter?
import 'package:flutter/material.dart';
import 'dart:ui';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Counter App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Counter App'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
int _counter = 0;
AnimationController controller;
bool _isButtonDisabled;
Duration get duration => controller.duration * controller.value;
bool get expired => duration.inSeconds == 0;
#override
void initState() {
controller = AnimationController(
vsync: this,
duration: Duration(seconds: 20),
);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'number $_counter added',
),
AnimatedBuilder(
animation: controller,
builder: (BuildContext context, Widget child) {
return new Text(
'${duration.inSeconds}',
style: new TextStyle(
fontWeight: FontWeight.bold,
fontSize: 50.0,
),
);
}),
new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new RaisedButton(
padding: const EdgeInsets.all(15.0),
textColor: Colors.white,
color: Colors.redAccent,
onPressed: () {
setState(() {
controller.reset();
_counter = 0;
});
},
child: new Text("Reset"),
),
new RaisedButton(
onPressed: () => setState(() {
controller.reverse(from: 1);
}),
textColor: Colors.white,
color: Colors.purple,
padding: const EdgeInsets.all(15.0),
child: new Text(
"Start",
),
),
],
),
],
),
),
bottomNavigationBar: BottomAppBar(
child: Container(
height: 50.0,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() {
_counter++;
}),
tooltip: 'Increment Counter',
child: Icon(Icons.add),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
you can use anywhere this code.
Timer(Duration(seconds: 30), () {
//checkFirstSeen(); your logic
});
try the following:
import 'package:flutter/material.dart';
import 'dart:ui';
import 'dart:async';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Counter App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Counter App'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
int _counter = 0;
AnimationController controller;
Duration get duration => controller.duration * controller.value;
bool get expired => duration.inSeconds == 0;
#override
void initState() {
controller = AnimationController(
vsync: this,
duration: Duration(seconds: 20),
);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedBuilder(
animation: controller,
builder: (BuildContext context, Widget child) {
return new Text(
'${duration.inSeconds}',
style: new TextStyle(
fontWeight: FontWeight.bold,
fontSize: 50.0,
),
);
}),
new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new RaisedButton(
padding: const EdgeInsets.all(15.0),
textColor: Colors.white,
color: Colors.redAccent,
onPressed: () {
setState(() {
controller.reset();
});
},
child: new Text("Reset"),
),
],
),
],
),
),
bottomNavigationBar: BottomAppBar(
child: Container(
height: 50.0,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() {
controller.reverse(from: 1);
}),
tooltip: 'Increment Counter',
child: Icon(Icons.add),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
When I use TextEditingController in CupertinoTextField, and change to another widget(page) and return, the previous state in that page is lost.
When I uncomment //controller: textController, everything works fine.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'test',
home: DefaultTabController(
length: 2,
child: Scaffold(
body: TabBarView(
children: [new Search(), new Setting(),
],
),
bottomNavigationBar: Container(
height: 60,
child: new TabBar(
tabs: [
Tab(icon: new Icon(Icons.search)),
Tab(icon: new Icon(Icons.settings)),
],
labelColor: Colors.blue,
unselectedLabelColor: Colors.grey,
),
)
),
),
);
}
}
class Setting extends StatelessWidget {
#override
Widget build(BuildContext context) {
return IconButton(
icon: Icon(Icons.check),
onPressed: () {
Navigator.push(context, CupertinoPageRoute(
builder: (context) =>
new Scaffold(
appBar: AppBar(title: Text('3'),),
)));
});
}
}
class Search extends StatefulWidget {
#override
createState() => new SearchState();
}
class SearchState extends State<Search> {
String currentWord = '';
final TextEditingController textController = new TextEditingController();
#override
void dispose() {
textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Row(
children: <Widget>[
new Expanded(
child: new CupertinoTextField(
style: TextStyle(color: Colors.white),
cursorColor: Colors.white,
//controller: textController,
maxLines: 1,
clearButtonMode: OverlayVisibilityMode.editing,
onChanged: (text) {
setState(() {
currentWord = text;
});
},
),
),
],
),
),
body: ListView.builder(
itemCount: 5,
itemBuilder: (context, i) {
return Text(currentWord);
})
);
}
}
The expected result(without controller set):get back and the state keeps the same.
Actual results(with controller set): get back and the state lost
The explanation for the observed behavior is the following:
CupertinoTextField uses an internal TextEditingController for which the framework automatically sets an AutomaticKeepAlive. This keepAlive is responsible for keeping the state.
If you use your own controller, you are in charge of attaching the AutomaticKeepAlive because the framework doesn't do it for you.
The following snippet adds the keepAlive to your code:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'test',
home: DefaultTabController(
length: 2,
child: Scaffold(
body: TabBarView(
children: [
new Search(),
new Setting(),
],
),
bottomNavigationBar: Container(
height: 60,
child: new TabBar(
tabs: [
Tab(icon: new Icon(Icons.search)),
Tab(icon: new Icon(Icons.settings)),
],
labelColor: Colors.blue,
unselectedLabelColor: Colors.grey,
),
)),
),
);
}
}
class Setting extends StatelessWidget {
#override
Widget build(BuildContext context) {
return IconButton(
icon: Icon(Icons.check),
onPressed: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => new Scaffold(
appBar: AppBar(
title: Text('3'),
),
)));
});
}
}
class Search extends StatefulWidget {
#override
createState() => new SearchState();
}
class SearchState extends State<Search> with AutomaticKeepAliveClientMixin {
String currentWord = '';
final TextEditingController textController = new TextEditingController();
#override
void initState() {
super.initState();
textController?.addListener(updateKeepAlive);
}
#override
void dispose() {
textController?.removeListener(updateKeepAlive);
textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
super.build(context); // See AutomaticKeepAliveClientMixin.
return new Scaffold(
appBar: new AppBar(
title: new Row(
children: <Widget>[
new Expanded(
child: new CupertinoTextField(
style: TextStyle(color: Colors.white),
cursorColor: Colors.white,
controller: textController,
maxLines: 1,
clearButtonMode: OverlayVisibilityMode.editing,
onChanged: (text) {
setState(() {
currentWord = text;
});
},
),
),
],
),
),
body: ListView.builder(
itemCount: 5,
itemBuilder: (context, i) {
return Text(currentWord);
}));
}
#override
bool get wantKeepAlive => textController?.text?.isNotEmpty == true;
}