'Tabs doesn't work with database loading error
I would like to create Tabs with including notes tab with sql. When I launch my flutter app it downloads values from database, but when I change my tabs from first to second or from third to second it goes for permanent loading state with error in terminal. Another issue is when I click save it doesn't pop back to tabs view automatically.
E/flutter (14975): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: DatabaseException(error database_closed)
main
import 'package:flutter/material.dart';
import 'package:learning_manager_flutter_ver1/notes/page/notes_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: TabsView(),
);
}
}
tabsview
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:learning_manager_flutter_ver1/notes/page/notes_page.dart';
class TabsView extends StatefulWidget {
const TabsView({Key? key}) : super(key: key);
@override
_TabsViewState createState() => _TabsViewState();
}
class _TabsViewState extends State<TabsView> {
@override
Widget build(BuildContext context) => DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
foregroundColor: Colors.black,
title: Text("Learning Manager"),
// actions: [
// IconButton(
// icon: Icon(Icons.notifications_none),
// color: AppColors.PRIMARY_COLOR,
// onPressed: () {
// print("Go to Notification center");
// }),
// IconButton(
// icon: Icon(Icons.archive),
// color: AppColors.PRIMARY_COLOR,
// onPressed: () {
// print("Go to Archive");
// },
// ),
// IconButton(
// color: AppColors.PRIMARY_COLOR,
// icon: Icon(Icons.settings),
// onPressed: () {
// print("Go to Settings");
// },
// )
// ],
backgroundColor: Colors.white,
// flexibleSpace: Container(
// decoration: BoxDecoration(
// gradient: LinearGradient(
// colors: [Colors.purple, Colors.red],
// begin: Alignment.bottomRight,
// end: Alignment.topLeft,
// ),
// ),
// ),
bottom: TabBar(
//isScrollable: true,
unselectedLabelColor: Colors.grey,
labelColor: Colors.black,
indicatorColor: Colors.black,
indicatorWeight: 5,
tabs: [
Tab(text: 'Inspiration'),
Tab(text: 'Notes'),
Tab(text: 'Goals'),
],
),
elevation: 20,
titleSpacing: 20,
automaticallyImplyLeading: false),
body: TabBarView(
children: [
buildPage('Inspiration'),
NotesPage(),
buildPage('Goals test')
],
),
),
);
Widget buildPage(String text) => Center(
child: Text(
text,
style: TextStyle(fontSize: 28),
),
);
}
notes_page
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import '../db/notes_database.dart';
import '../model/note.dart';
import '../widget/note_card_widget.dart';
import 'edit_note_page.dart';
import 'note_detail_page.dart';
class NotesPage extends StatefulWidget {
@override
_NotesPageState createState() => _NotesPageState();
}
class _NotesPageState extends State<NotesPage> {
late List<Note> notes;
bool isLoading = false;
@override
void initState() {
super.initState();
refreshNotes();
}
@override
void dispose() {
NotesDatabase.instance.close();
super.dispose();
}
Future refreshNotes() async {
setState(() => isLoading = true);
this.notes = await NotesDatabase.instance.readAllNotes();
setState(() => isLoading = false);
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text(
'Notes',
style: TextStyle(fontSize: 24),
),
actions: [Icon(Icons.search), SizedBox(width: 12)],
),
body: Center(
child: isLoading
? CircularProgressIndicator()
: notes.isEmpty
? Text(
'No Notes',
style: TextStyle(color: Colors.white, fontSize: 24),
)
: buildNotes(),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.black,
child: Icon(Icons.add),
onPressed: () async {
await Navigator.of(context).push(
MaterialPageRoute(builder: (context) => AddEditNotePage()),
);
refreshNotes();
},
),
);
Widget buildNotes() => StaggeredGridView.countBuilder(
padding: EdgeInsets.all(8),
itemCount: notes.length,
staggeredTileBuilder: (index) => StaggeredTile.fit(2),
crossAxisCount: 4,
mainAxisSpacing: 4,
crossAxisSpacing: 4,
itemBuilder: (context, index) {
final note = notes[index];
return GestureDetector(
onTap: () async {
await Navigator.of(context).push(MaterialPageRoute(
builder: (context) => NoteDetailPage(noteId: note.id!),
));
refreshNotes();
},
child: NoteCardWidget(note: note, index: index),
);
},
);
}
notes detail page
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../db/notes_database.dart';
import '../model/note.dart';
import 'edit_note_page.dart';
class NoteDetailPage extends StatefulWidget {
final int noteId;
const NoteDetailPage({
Key? key,
required this.noteId,
}) : super(key: key);
@override
_NoteDetailPageState createState() => _NoteDetailPageState();
}
class _NoteDetailPageState extends State<NoteDetailPage> {
late Note note;
bool isLoading = false;
@override
void initState() {
super.initState();
refreshNote();
}
Future refreshNote() async {
setState(() => isLoading = true);
this.note = await NotesDatabase.instance.readNote(widget.noteId);
setState(() => isLoading = false);
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
actions: [editButton(), deleteButton()],
),
body: isLoading
? Center(child: CircularProgressIndicator())
: Padding(
padding: EdgeInsets.all(12),
child: ListView(
padding: EdgeInsets.symmetric(vertical: 8),
children: [
Text(
note.title,
style: TextStyle(
color: Colors.black,
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
Text(
DateFormat.yMMMd().format(note.createdTime),
style: TextStyle(color: Colors.white38),
),
SizedBox(height: 8),
Text(
note.description,
style: TextStyle(color: Colors.white70, fontSize: 18),
)
],
),
),
);
Widget editButton() => IconButton(
icon: Icon(Icons.edit_outlined),
onPressed: () async {
if (isLoading) return;
await Navigator.of(context).push(MaterialPageRoute(
builder: (context) => AddEditNotePage(note: note),
));
refreshNote();
});
Widget deleteButton() => IconButton(
icon: Icon(Icons.delete),
onPressed: () async {
await NotesDatabase.instance.delete(widget.noteId);
Navigator.of(context).pop();
},
);
}
edit note page
import 'package:flutter/material.dart';
import '../db/notes_database.dart';
import '../model/note.dart';
import '../widget/note_form_widget.dart';
class AddEditNotePage extends StatefulWidget {
final Note? note;
const AddEditNotePage({
Key? key,
this.note,
}) : super(key: key);
@override
_AddEditNotePageState createState() => _AddEditNotePageState();
}
class _AddEditNotePageState extends State<AddEditNotePage> {
final _formKey = GlobalKey<FormState>();
late bool isImportant;
late int number;
late String title;
late String description;
@override
void initState() {
super.initState();
isImportant = widget.note?.isImportant ?? false;
number = widget.note?.number ?? 0;
title = widget.note?.title ?? '';
description = widget.note?.description ?? '';
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
actions: [buildButton()],
),
body: Form(
key: _formKey,
child: NoteFormWidget(
isImportant: isImportant,
number: number,
title: title,
description: description,
onChangedImportant: (isImportant) =>
setState(() => this.isImportant = isImportant),
onChangedNumber: (number) => setState(() => this.number = number),
onChangedTitle: (title) => setState(() => this.title = title),
onChangedDescription: (description) =>
setState(() => this.description = description),
),
),
);
Widget buildButton() {
final isFormValid = title.isNotEmpty && description.isNotEmpty;
return Padding(
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 12),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
onPrimary: Colors.white,
primary: isFormValid ? null : Colors.grey.shade700,
),
onPressed: addOrUpdateNote,
child: Text('Save'),
),
);
}
void addOrUpdateNote() async {
final isValid = _formKey.currentState!.validate();
if (isValid) {
final isUpdating = widget.note != null;
if (isUpdating) {
await updateNote();
} else {
await addNote();
}
Navigator.of(context).pop();
}
}
Future updateNote() async {
final note = widget.note!.copy(
isImportant: isImportant,
number: number,
title: title,
description: description,
);
await NotesDatabase.instance.update(note);
}
Future addNote() async {
final note = Note(
title: title,
isImportant: true,
number: number,
description: description,
createdTime: DateTime.now(),
);
await NotesDatabase.instance.create(note);
}
}
It is not only text, but necessary code of my application. The problem occurs with database so if more code is needed I would with a pleasure send you more details. Please write in the comment what you need.
Solution 1:[1]
Database Error (database_closed)
I am not sure how NotesDatabase is implemented but make sure you consider these points.
NotesDatabase.instancecan always initialize a new instance if the database has not yet initialized or already disposed.- In
_NotesPageState.dispose(), there isNotesDatabase.instance.close();. If this will close yourNotesDatabaseconnection permanently, don't close it, or handleNotesDatabase.instanceproperly.
Endless loading
Due to the error thrown by NotesDatabase, setState(() => isLoading = false); will not be executed.
Consider some exception handling for all API calls or DB queries. These actions rely on network stability, write some defensive code to avoid unpleasant experiences. Either handle exceptions in NotesDatabase methods or try catch them.
Future refreshNote() async {
setState(() => isLoading = true);
try {
this.note = await NotesDatabase.instance.readNote(widget.noteId);
} catch (e) {
// Handle error here, either show some information or dialog.
// print() is only for debugging purpose.
print(e);
} finally {
setState(() => isLoading = false);
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | TYJ |


