Whenever I launch/re-launch my application on a real device the screen doesn't load or render the widgets until I make some touch inputs like scrolling the list (that is present in the home screen but not yet visible/rendered) or swiping between screens (the tabs under tabbar/pageview), without these manual touch inputs the app doesn't render these widgets automatically. It works fine on the emulator, the issue only exists on a real device for me. I found a similar thread here which might help understand the problem a bit better.
The main screen of the application fetches data from a local SQLite Database and displays it in a ListView, I use provider to fetch data and update the UI, and I use FutureBuilder
to build the widgets.
I am aware that the TabBar doesn't persist state and I tried to fix it using AutomaticKeepAliveClientMixin
in all my Stateful Widgets under the TabBar as shown here, but that too didn't work for me. Everything works fine for me when I'm just using a BottomNavBar or just without the TabBar or PageView.
I also tried to use PageController with BottomNavBar because I want that swipe to change tabs feature, but I'm facing the same issue again. Here's the code:
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../../widgets/custom_scaffold.dart';
import './all_tasks_list.dart';
import './todays_task_list.dart';
import './task_input_field.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final PageController _pageController = PageController();
int _activePage = 0;
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
List<String> appBarTitles = [
DateFormat('EEE, MMM d').format(DateTime.now()),
'Summary'
];
@override
Widget build(BuildContext context) {
return MyScaffold(
appBarTitle: appBarTitles[_activePage],
body: PageView(
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
controller: _pageController,
children: const [
TodaysTasks(),
AllTasksList(),
],
onPageChanged: (index) {
setState(() {
_activePage = index;
});
},
),
bottomNavigationBar: BottomNavigationBar(
selectedIconTheme: const IconThemeData(size: 35),
selectedFontSize: 0,
currentIndex: _activePage,
onTap: (index) {
_pageController.animateToPage(
index,
duration: const Duration(milliseconds: 350),
curve: Curves.ease,
);
},
type: BottomNavigationBarType.shifting,
items: [
BottomNavigationBarItem(
icon: const Icon(Icons.task),
label: 'Today',
backgroundColor: Theme.of(context).hintColor,
),
BottomNavigationBarItem(
icon: const Icon(Icons.history),
label: 'History',
backgroundColor: Theme.of(context).hintColor,
),
],
),
);
}
}
class TodaysTasks extends StatefulWidget {
const TodaysTasks({Key? key}) : super(key: key);
@override
State<TodaysTasks> createState() => _TodaysTasksState();
}
class _TodaysTasksState extends State<TodaysTasks>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
return Column(
children: const [
Flexible(child: TodaysTasksList()),
TaskInputTextField(),
],
);
}
@override
bool get wantKeepAlive => true;
}
all_tasks_list.dart:
// All Imports...
class AllTasksList extends StatefulWidget {
const AllTasksList({Key? key}) : super(key: key);
@override
State<AllTasksList> createState() => _AllTasksListState();
}
class _AllTasksListState extends State<AllTasksList>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
return FutureBuilder(
future:
Provider.of<TaskProvider>(context, listen: false).fetchAndSetTasks(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
List<TaskModel> listOfTasks =
Provider.of<TaskProvider>(context).getAllTasks;
return ListView.builder(
itemCount: listOfTasks.length,
itemBuilder: (context, index) => ListTile(
title: Text(listOfTasks[index].taskTitle),
subtitle:
Text(DateMethods.dateFormatter(listOfTasks[index].timeStamp)),
),
);
} else {
return const Center(child: CircularProgressIndicator());
}
},
);
}
@override
bool get wantKeepAlive => true;
}
todays_task_list.dart:
// Imports...
class TodaysTasksList extends StatefulWidget {
const TodaysTasksList({Key? key}) : super(key: key);
@override
State<TodaysTasksList> createState() => _TodaysTasksListState();
}
class _TodaysTasksListState extends State<TodaysTasksList>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
return FutureBuilder(
future: Provider.of<TaskProvider>(context, listen: false)
.fetchAndSetTodaysTasks(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
List<TaskModel> listOfTasks =
Provider.of<TaskProvider>(context).getTasks;
if (listOfTasks.isEmpty) {
return const Center(
child: Text('No tasks added today'),
);
} else {
return ListView.builder(
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
itemCount: listOfTasks.length,
itemBuilder: (context, index) => ChangeNotifierProvider.value(
key: ValueKey(listOfTasks[index].timeStamp.toIso8601String()),
value: listOfTasks[index],
child: const TaskItem(),
),
);
}
} else {
return const Center(child: CircularProgressIndicator());
}
},
);
}
@override
bool get wantKeepAlive => true;
}
class TaskItem extends StatefulWidget {
const TaskItem({
Key? key,
}) : super(key: key);
@override
State<TaskItem> createState() => _TaskItemState();
}
class _TaskItemState extends State<TaskItem>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
final taskItem = Provider.of<TaskModel>(context, listen: false);
return ListTile(
title: Consumer<TaskModel>(
builder: (context, value, _) => TaskTitle(taskItem: taskItem),
),
subtitle: Text(
DateMethods.dateFormatter(taskItem.timeStamp),
),
trailing: Consumer<TaskModel>(
builder: (context, value, child) => TrailingIcon(taskItem: taskItem)),
);
}
@override
bool get wantKeepAlive => true;
}
class TaskTitle extends StatefulWidget {
final TaskModel taskItem;
const TaskTitle({Key? key, required this.taskItem}) : super(key: key);
@override
State<TaskTitle> createState() => _TaskTitleState();
}
class _TaskTitleState extends State<TaskTitle>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
// returns const Widgets to be displayed
}
}
@override
bool get wantKeepAlive => true;
}
class TrailingIcon extends StatefulWidget {
final TaskModel taskItem;
const TrailingIcon({Key? key, required this.taskItem}) : super(key: key);
@override
State<TrailingIcon> createState() => _TrailingIconState();
}
class _TrailingIconState extends State<TrailingIcon>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
// returns const Widgets to be displayed
}
}
@override
bool get wantKeepAlive => true;
void deleteTask(BuildContext context, String taskId) {
Provider.of<TaskProvider>(context, listen: false).deleteTask(taskId);
}
}
task_input_field.dart:
// Imports...
class TaskInputTextField extends StatefulWidget {
const TaskInputTextField({Key? key}) : super(key: key);
@override
State<TaskInputTextField> createState() => _TaskInputTextFieldState();
}
class _TaskInputTextFieldState extends State<TaskInputTextField>
with AutomaticKeepAliveClientMixin {
final TextEditingController _taskController = TextEditingController();
@override
void dispose() {
_taskController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
super.build(context);
return Container(
margin: const EdgeInsets.symmetric(horizontal: 10),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).hintColor, width: 2),
borderRadius: BorderRadius.circular(15),
),
child: TextField(
// TextField Decoration code
),
//Just some usual TextField stuff
),
);
}
@override
bool get wantKeepAlive => true;
void addTask() {
if (_taskController.text.isEmpty) {
finishingUp();
return;
}
TaskModel taskData =
TaskModel(taskTitle: _taskController.text, timeStamp: DateTime.now());
Provider.of<TaskProvider>(context, listen: false).addTask(taskData);
finishingUp();
}
void finishingUp() {
_taskController.clear();
FocusScope.of(context).unfocus();
}
}