// 이곳에 쓴 내용은 앱 만들기라는 버킷리스트를 달성하기 위해 플러터를 공부하면서 정리하고 있는 내용입니다.
플러터에 대해 아는 것이 거의 없기 때문에 정리하면서 오류가 있을 수 있습니다.
오류를 발견하신 분은 댓글 남겨 주시면 감사하겠습니다.
데이터베이스 데이터 저장하기
sqflite: ^2.3.3+1, path: ^1.9.0, pubspec.yaml에 추가
https://pub.dev/packages/sqflite
주말인지 알아서 그런지 정말 팍센 코드다. 오류를 도대체 몇 군데를 발견하고, 수정하고, 보고 타이핑하는데도 이렇게 힘들다니. 더군다나 작동을 잘 안한다.
main.dart
import 'package:flutter/material.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'addTodo.dart';
import 'clearList.dart';
import 'todo.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
Future<Database> database = initDatabase();
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: '/',
routes: {
'/': (context) => DatabaseApp(database),
'/add': (context) => AddTodoApp(database),
'/clear': (context) => ClearListApp(database)
},
);
}
Future<Database> initDatabase() async {
return openDatabase(
join(await getDatabasesPath(), 'todo_database.db'),
onCreate: (db, version) {
return db.execute(
"CREATE TABLE todos(id INTEGER PRIMARY KEY AUTOINCREMENT,"
"title TEXT, content TEXT, active INTEGGER)",
);
},
version: 1,
);
}
}
class DatabaseApp extends StatefulWidget {
final Future<Database> db;
const DatabaseApp(this.db, {super.key});
@override
State<DatabaseApp> createState() => _DatabaseAppState();
}
class _DatabaseAppState extends State<DatabaseApp> {
Future<List<Todo>>? todoList;
@override
void initState() {
super.initState();
todoList = getTodos();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Database Example'), actions: <Widget>[
ElevatedButton(
onPressed: () async {
await Navigator.of(context).pushNamed('/clear');
setState(() {
todoList = getTodos();
});
},
child: const Text(
'완료한 일',
style: TextStyle(color: Colors.black),
))
]),
body: Center(
child: FutureBuilder(
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return const CircularProgressIndicator();
case ConnectionState.waiting:
return const CircularProgressIndicator();
case ConnectionState.active:
return const CircularProgressIndicator();
case ConnectionState.done:
if (snapshot.hasData) {
return ListView.builder(
itemBuilder: (context, index) {
Todo todo = (snapshot.data as List<Todo>)[index];
return ListTile(
title: Text(
todo.title!,
style: const TextStyle(fontSize: 20),
),
subtitle: Column(
children: <Widget>[
Text(todo.content!),
Text('체크 : ${todo.active == 1
? 'true'
: 'false'}'),
Container(
height: 1,
color: Colors.blue,
)
],
),
onTap: () async {
Todo result = await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('${todo.id} : ${todo.title}'),
content: const Text('Todo를 체크하시겠습니까?'),
actions: <Widget>[
TextButton(
onPressed: () {
setState(() {
todo.active == 1
? todo.active = 0 : todo
.active = 1;
});
Navigator.of(context).pop(todo);
},
child: const Text('예')),
TextButton(
onPressed: () {
Navigator.of(context).pop(todo);
},
child: const Text('아니오')),
],
);
});
_updateTodo(result);
},
onLongPress: () async {
Todo result = await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('${todo.id}:${todo.title}'),
content:
Text('${todo.content}를 삭제하시겠습니까?'),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop(todo);
},
child: const Text('예')),
TextButton(
onPressed: () {
Navigator.of(context).pop(todo);
},
child: const Text('아니요')),
],
);
});
_deleteTodo(result);
},
);
},
itemCount: (snapshot.data as List<Todo>).length,
);
} else {
return const Text('No data');
}
}
},
future: todoList,
),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(onPressed: () async {
final todo = await Navigator.of(context).pushNamed('/add');
if (todo != null) {
_insertTodo(todo as Todo);
}
},
heroTag: null,
child: const Icon(Icons.add),
),
const SizedBox(
height: 10,),
FloatingActionButton(
onPressed: () async {
_allUpdate();
},
heroTag: null,
child: const Icon(Icons.update),
),
],
));
}
void _allUpdate() async {
final Database database = await widget.db;
await database.rawUpdate('update todos set active = 1 where active = 0');
setState(() {
todoList = getTodos();
});
}
void _deleteTodo(Todo todo) async{
final Database database = await widget.db;
await database.delete('todos', where: 'id=?', whereArgs: [todo.id]);
setState((){
todoList = getTodos();
});
}
void _updateTodo(Todo todo) async {
final Database database = await widget.db;
await database.update(
'todos', todo.toMap(), where: 'id = ?', whereArgs: [todo.id],);
setState(() {
todoList = getTodos();
});
}
void _insertTodo(Todo todo) async {
final Database database = await widget.db;
await database.insert('todos', todo.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace);
setState(() {
todoList = getTodos();
});
}
Future<List<Todo>> getTodos() async {
final Database database = await widget.db;
final List<Map<String, dynamic>> maps = await database.query('todos');
return List.generate(maps.length, (i) {
int active = maps[i]['active'] == 1 ? 1 : 0;
return Todo(
title: maps[i]['title'].toString(),
content: maps[i]['content'].toString(),
active: active,
id: maps[i]['id']);
});
}
}
todo.dart
class Todo{
String? title;
String? content;
int? active;
int? id;
Todo({this.title, this.content, this.active, this.id});
Map<String, dynamic> toMap(){
return {
'id': id,
'title': title,
'content': content,
'active': active,
};
}
}
clearList.dart
import 'package:flutter/material.dart';
import 'package:sqflite/sqlite_api.dart';
import 'package:doit10_1/todo.dart';
class ClearListApp extends StatefulWidget {
Future<Database> database;
ClearListApp(this.database, {super.key});
@override
State<StatefulWidget> createState() => _ClearListApp();
}
class _ClearListApp extends State<ClearListApp> {
Future<List<Todo>>? clearList;
@override
void initState() {
super.initState();
clearList = getClearList();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('이미 한일'),
),
body: Center(
child: FutureBuilder(
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return const CircularProgressIndicator();
case ConnectionState.waiting:
return const CircularProgressIndicator();
case ConnectionState.active:
return const CircularProgressIndicator();
case ConnectionState.done:
if (snapshot.hasData) {
return ListView.builder(
itemBuilder: (context, index) {
Todo todo = (snapshot.data as List<Todo>)[index];
return ListTile(
title: Text(
todo.title!,
style: const TextStyle(fontSize: 20),
),
subtitle: Column(
children: <Widget>[
Text(todo.content!),
Container(
height: 1,
color: Colors.blue,
)
],
),
);
},
itemCount: (snapshot.data as List<Todo>).length,
);
}
}
return const Text('No data');
},
future: clearList,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
final result = await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('완료한 일 삭제'),
content: const Text('완료한 일을 모두 삭제할까요?'),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop(true);
},
child: const Text('예')),
TextButton(
onPressed: () {
Navigator.of(context).pop(false);
},
child: const Text('아니요')),
],
);
});
if (result == true) {
_removeAllTodos();
}
},
child: const Icon(Icons.remove),
),
);
}
Future<List<Todo>> getClearList() async {
final Database database = await widget.database;
List<Map<String, dynamic>> maps = await database
.rawQuery('select title, content, id from todos shere active=1');
return List.generate(maps.length, (i) {
return Todo(
title: maps[i]['title'].toString(),
content: maps[i]['content'].toString(),
id: maps[i]['id']);
});
}
void _removeAllTodos() async {
final Database database = await widget.database;
database.rawDelete('delete from todos wher active=1');
setState(() {
clearList = getClearList();
});
}
}
addTodo.dart
import 'package:flutter/material.dart';
import 'package:sqflite/sqlite_api.dart';
import 'todo.dart';
class AddTodoApp extends StatefulWidget {
final Future<Database> db;
const AddTodoApp(this.db, {super.key});
@override
State<AddTodoApp> createState() => _AddTodoAppState();
}
class _AddTodoAppState extends State<AddTodoApp> {
TextEditingController? titleController;
TextEditingController? contentController;
@override
void initState() {
super.initState();
titleController = TextEditingController();
contentController = TextEditingController();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Todo 추가'),
),
body: Center(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(10),
child: TextField(
controller: titleController,
decoration: const InputDecoration(labelText: '제목'),
),
),
Padding(
padding: const EdgeInsets.all(10),
child: TextField(
controller: contentController,
decoration: const InputDecoration(labelText: '할일'),
),
),
ElevatedButton(
onPressed: () {
Todo todo = Todo(
title: titleController!.value.text,
content: contentController!.value.text,
active: 0);
Navigator.of(context).pop(todo);
},
child: const Text('저장하기'),
)
],
),
),
);
}
}
완료한 일에 아무것도 안 뜨는 문제가 발생한다. 체크에 true로 바뀌면 완료한 일로 보내져야 하는데, 그렇게 안된다.
그것말고도 작동 안되는 게 있는 듯하다.
어쨌든 오늘은 여기까지..
질문은 데이터베이스라 함은 외부 저장소인 줄 알았는데, 아닌가 보다. 내부 저장소도 데이터베이스라고 부르는건가?
그리고 자꾸 에뮬레이터에 자꾸 이런 오류 메세지가 뜬다.
System UI isn't responding
컴퓨터가 꼬지면 그렇단다.
안드로이드 스튜디오 끄고, 윈도우 내컴퓨터를 실행한 후, userdata-qemu를 삭제하면 해결된다. 다만, 이렇게 했을 경우 에뮬에 설정했던 모든 정보가 날아간다.
에뮬에 앱들이 자꾸 추가되니깐 문제가 되는 거 같다. 주기적으로 삭제를 하던지, 아니면 설치했던 앱들을 삭제해 보는 방법을 선택해 봐야겠다.
'버킷리스트 > 앱 만들기' 카테고리의 다른 글
오공완 (with Do it! 플러터 앱 프로그램밍) #12_1 (0) | 2024.05.15 |
---|---|
오공완 (with Do it! 플러터 앱 프로그램밍) #11 (0) | 2024.05.13 |
오공완 (with Do it! 플러터 앱 프로그램밍) #9_2 (0) | 2024.05.12 |
오공완 (with Do it! 플러터 앱 프로그램밍) #9_1 (0) | 2024.05.10 |
오공완 (with Do it! 플러터 앱 프로그램밍) #8_1 (0) | 2024.05.09 |