I am currently developing a flutter desktop CRM and I want to implement a multi tab feature. But when I change into another tab and then back to the first flutter rebuild the whole widget. So when write some data to the text input inside tab "Tab appointmentsKey"(Image 1) and then I change into another tab(Image 2) and then back to tab "Tab appointmentsKey", my data is lost(Image 3).
I am using fluent UI Library link for the Tab Widget ( TabView Widget )
Question:
Is there a way to ignore rebuild when I change between the tabs?
Here my $flutter doctor
:
[√] Flutter (Channel stable, 3.0.5, on Microsoft Windows [Version 10.0.19044.1889], locale en-US)
[√] Android toolchain - develop for Android devices (Android SDK version 31.0.0)
[√] Chrome - develop for the web
[√] Visual Studio - develop for Windows (Visual Studio Community 2022 17.2.6)
[√] Android Studio (version 2020.3)
[√] VS Code (version 1.70.2)
[√] VS Code, 64-bit edition (version 1.20.1)
[√] Connected device (3 available)
[√] HTTP Host Availability
Here my $flutter --version
:
Flutter 3.0.5 • channel stable • https://github.com/flutter/flutter.git
Framework • revision f1875d570e (6 weeks ago) • 2022-07-13 11:24:16 -0700
Engine • revision e85ea0e79c
Tools • Dart 2.17.6 • DevTools 2.12.2
Here snippets:
home.dart (host of the tabView widget ):
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home>{
//TODO replace isLoaded with false
bool isLoaded = true;
bool serverProblem = false;
int currentIndex = 0;
final AppointmentGlobalKey = GlobalKey();
final DashboardGlobalKey = GlobalKey();
late Map<String, Map<String, dynamic>> mapScreenRoutes;
@override
void initState() {
mapScreenRoutes = {
App.appointmentsKey: {
"title": "Appointments",
"screen": AppointmentsScreen(),
},
App.dashboardKey: {
"title": "Dashboard",
"screen": DashBoardScreen(),
},
};
super.initState();
}
@override
Widget build(BuildContext context) {
return isLoaded == false
? LoadingScreen(serverProblem: serverProblem)
: Scaffold(
body: Row(
children: [
const SizedBox(
width: 260,
height: double.infinity,
child: VerticalNavBar(),
),
Expanded(
child: Column(
children: [
const HeaderSearchAccount(),
Expanded(
child: SizedBox(
child: fui.TabView(
closeButtonVisibility:
fui.CloseButtonVisibilityMode.always,
currentIndex: currentIndex,
onChanged: (index) => setState(() {
currentIndex = index;
}),
onNewPressed: null,
tabs: [
fui.Tab(
text: Text(
mapScreenRoutes[App.appointmentsKey]
?['title']),
closeIcon: fui.FluentIcons.chrome_close,
),
fui.Tab(
text: Text(mapScreenRoutes[App.dashboardKey]
?['title']),
closeIcon: fui.FluentIcons.chrome_close,
)
],
bodies: [
AppointmentsScreen(
key: AppointmentGlobalKey,
),
DashBoardScreen(
key: DashboardGlobalKey,
),
]),
),
),
],
),
)
],
),
);
}
}
And the two Widget that are inside in each tab body:
appointments_screen.dart:
class AppointmentsScreen extends StatefulWidget {
const AppointmentsScreen({Key? key}) : super(key: key);
@override
State<AppointmentsScreen> createState() => _AppointmentsScreenState();
}
class _AppointmentsScreenState extends State<AppointmentsScreen> {
bool isLoaded = false;
bool serverProblem = false;
@override
void didChangeDependencies() {
Api.get().fetchTextTest().then((value) {
print(value);
setState(() {
isLoaded = true;
});
});
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
final emailController = TextEditingController();
final formKey = GlobalKey<FormState>();
return isLoaded == false
? LoadingScreen(serverProblem: serverProblem)
: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Appointments()'),
Center(
child: Form(
key: formKey,
child: TextFormField(
keyboardType: TextInputType.emailAddress,
controller: emailController,
autofocus: false,
decoration: InputDecoration(
hintText: 'Email',
contentPadding:
EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0)),
),
),
),
),
],
);
}
}
dashboard_screen.dart:
class DashBoardScreen extends StatefulWidget {
const DashBoardScreen({Key? key}) : super(key: key);
@override
State<DashBoardScreen> createState() => _DashBoardScreenState();
}
class _DashBoardScreenState extends State<DashBoardScreen>
with AutomaticKeepAliveClientMixin {
//TODO replace isLoaded with false
bool isLoaded = true;
bool serverProblem = false;
@override
void initState() {
super.initState();
}
@override
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
return isLoaded == false
? LoadingScreen(serverProblem: serverProblem)
: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text('DashBoardScreen()'),
],
);
}
}
3
Answers
No exact idea about the library you mentioned.
But if possible you can define Key for your children’s tabs views, so that it will keep the state, and you can avoid rebuild of widgets.
example:
final childKeyTab1 = GlobalKey();
YourCustomStateFullWidget(key: childKeyTab1);
Update:
I checked your code again, you are initiating TextEditingController under the build method, that is wrong you need to initiate it under initState function or out of the build method
Please follow the github issue
https://github.com/flutter/flutter/issues/19116
Add AutomaticKeepAliveClientMixin to each of the Tab widgets.