-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
552 additions
and
256 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
TURSO_URL= | ||
TURSO_TOKEN= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,13 @@ | ||
# libsql_dart_example | ||
|
||
Demonstrates how to use the libsql_dart plugin. | ||
|
||
## Getting Started | ||
|
||
This project is a starting point for a Flutter application. | ||
Do either of the following: | ||
|
||
A few resources to get you started if this is your first Flutter project: | ||
- Create a remote Turso database | ||
- Run a local Turso database `https://docs.turso.tech/local-development#turso-cli` | ||
- Run a local libsql instance using the provided `docker-compose.yaml` | ||
|
||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) | ||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) | ||
Fill the credentials in `.env` | ||
|
||
For help getting started with Flutter development, view the | ||
[online documentation](https://docs.flutter.dev/), which offers tutorials, | ||
samples, guidance on mobile development, and a full API reference. | ||
Run the example `flutter run --debug --dart-define-from-file=.env` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
services: | ||
libsql: | ||
container_name: libsql_dart_sqld | ||
image: ghcr.io/tursodatabase/libsql-server:v0.24.23 | ||
restart: always | ||
ports: | ||
- 8081:8080 |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import 'package:libsql_dart/libsql_dart.dart'; | ||
|
||
Future<void> bootstrapDatabase(LibsqlClient client) async { | ||
await client.connect(); | ||
// await client.execute("drop table if exists tasks"); | ||
await client.execute( | ||
"create table if not exists tasks (id integer primary key, title text, description text, completed integer)"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export 'task_list_cubit.dart'; | ||
export 'task_list_state.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import 'package:flutter_bloc/flutter_bloc.dart'; | ||
import 'package:libsql_dart_example/features/task/blocs/task_list_state.dart'; | ||
import 'package:libsql_dart_example/features/task/models/models.dart'; | ||
import 'package:libsql_dart_example/features/task/repositories/repositories.dart'; | ||
|
||
class TaskListCubit extends Cubit<TaskListState> { | ||
final TaskRepository _taskRepository; | ||
|
||
TaskListCubit(this._taskRepository) : super(TaskListInitial()) { | ||
getTasks(); | ||
} | ||
|
||
Future<void> getTasks() async { | ||
emit(TaskListLoading()); | ||
try { | ||
final tasks = await _taskRepository.getTasks(); | ||
emit(TaskListLoaded(tasks)); | ||
} catch (e) { | ||
emit(TaskListError(e.toString())); | ||
} | ||
} | ||
|
||
Future<void> addTask(Task task) async { | ||
try { | ||
await _taskRepository.addTask(task); | ||
await getTasks(); | ||
} catch (e) { | ||
emit(TaskListError(e.toString())); | ||
} | ||
} | ||
|
||
Future<void> deleteTask(int id) async { | ||
try { | ||
await _taskRepository.deleteTask(id); | ||
await getTasks(); | ||
} catch (e) { | ||
emit(TaskListError(e.toString())); | ||
} | ||
} | ||
|
||
Future<void> markTasksAsCompleted(List<int> ids) async { | ||
try { | ||
await _taskRepository.markTasksAsCompleted(ids); | ||
await getTasks(); | ||
} catch (e) { | ||
emit(TaskListError(e.toString())); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import 'package:libsql_dart_example/features/task/models/models.dart'; | ||
|
||
abstract class TaskListState {} | ||
|
||
class TaskListInitial extends TaskListState {} | ||
|
||
class TaskListLoading extends TaskListState {} | ||
|
||
class TaskListLoaded extends TaskListState { | ||
final List<Task> tasks; | ||
TaskListLoaded(this.tasks); | ||
} | ||
|
||
class TaskListError extends TaskListState { | ||
final String message; | ||
TaskListError(this.message); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export 'task.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
class Task { | ||
final int id; | ||
final String title; | ||
final String description; | ||
final bool completed; | ||
|
||
const Task({ | ||
required this.id, | ||
required this.title, | ||
required this.description, | ||
required this.completed, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export 'task_repository.dart'; |
11 changes: 11 additions & 0 deletions
11
example/lib/features/task/repositories/task_repository.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import 'package:libsql_dart_example/features/task/models/models.dart'; | ||
|
||
abstract class TaskRepository { | ||
Future<List<Task>> getTasks(); | ||
|
||
Future<void> addTask(Task task); | ||
|
||
Future<void> deleteTask(int id); | ||
|
||
Future<void> markTasksAsCompleted(List<int> ids); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export 'blocs/blocs.dart'; | ||
export 'models/models.dart'; | ||
export 'repositories/repositories.dart'; | ||
export 'task_list.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import 'package:flutter/material.dart'; | ||
|
||
class TaskAdd extends StatefulWidget { | ||
const TaskAdd({super.key}); | ||
|
||
@override | ||
State<TaskAdd> createState() => _TaskAddState(); | ||
} | ||
|
||
class _TaskAddState extends State<TaskAdd> { | ||
final formKey = GlobalKey<FormState>(); | ||
final titleController = TextEditingController(); | ||
final descriptionController = TextEditingController(); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return BottomSheet( | ||
onClosing: () {}, | ||
enableDrag: false, | ||
builder: (context) { | ||
return SingleChildScrollView( | ||
child: Form( | ||
key: formKey, | ||
autovalidateMode: AutovalidateMode.disabled, | ||
child: Padding( | ||
padding: const EdgeInsets.all(24).add(EdgeInsets.only( | ||
bottom: MediaQuery.of(context).viewInsets.bottom)), | ||
child: Column( | ||
crossAxisAlignment: CrossAxisAlignment.stretch, | ||
children: [ | ||
const Text('Add new task', style: TextStyle(fontSize: 24)), | ||
const SizedBox(height: 16), | ||
const Text('Name'), | ||
TextFormField( | ||
controller: titleController, | ||
textCapitalization: TextCapitalization.sentences, | ||
onTap: () { | ||
titleController.selection = TextSelection( | ||
baseOffset: 0, | ||
extentOffset: titleController.text.length, | ||
); | ||
}, | ||
validator: (value) { | ||
if (value?.isEmpty ?? true) { | ||
return 'Must not be empty'; | ||
} | ||
return null; | ||
}, | ||
), | ||
const SizedBox(height: 16), | ||
const Text('Description'), | ||
TextFormField( | ||
controller: descriptionController, | ||
textCapitalization: TextCapitalization.sentences, | ||
onTap: () { | ||
titleController.selection = TextSelection( | ||
baseOffset: 0, | ||
extentOffset: titleController.text.length, | ||
); | ||
}, | ||
validator: (value) { | ||
if (value?.isEmpty ?? true) { | ||
return 'Must not be empty'; | ||
} | ||
return null; | ||
}, | ||
), | ||
const SizedBox(height: 32), | ||
FilledButton( | ||
onPressed: () { | ||
if (!formKey.currentState!.validate()) return; | ||
Navigator.maybePop(context, { | ||
'title': titleController.text, | ||
'description': descriptionController.text, | ||
}); | ||
}, | ||
child: const Text('Add task'), | ||
), | ||
], | ||
), | ||
), | ||
), | ||
); | ||
}, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_bloc/flutter_bloc.dart'; | ||
import 'package:libsql_dart_example/features/task/blocs/blocs.dart'; | ||
import 'package:libsql_dart_example/features/task/models/task.dart'; | ||
import 'package:libsql_dart_example/features/task/repositories/repositories.dart'; | ||
import 'package:libsql_dart_example/features/task/task_add.dart'; | ||
|
||
class TaskList extends StatefulWidget { | ||
const TaskList({super.key}); | ||
|
||
@override | ||
State<TaskList> createState() => _TaskListState(); | ||
} | ||
|
||
class _TaskListState extends State<TaskList> { | ||
@override | ||
Widget build(BuildContext context) { | ||
return BlocProvider( | ||
create: (context) => TaskListCubit(context.read<TaskRepository>()), | ||
child: SafeArea( | ||
child: Scaffold( | ||
floatingActionButton: Builder(builder: (context) { | ||
return FloatingActionButton( | ||
onPressed: () async { | ||
// final res = await memoryClient.execute( | ||
// "create table tasks (id integer primary key, title string, description string, completed integer);"); | ||
// print(await memoryClient.query("select * from tasks;")); | ||
// await context.read<TaskListCubit>().getTasks(); | ||
final taskData = | ||
await showModalBottomSheet<Map<String, dynamic>>( | ||
context: context, | ||
isScrollControlled: true, | ||
builder: (_) => const TaskAdd(), | ||
); | ||
if (taskData == null || !context.mounted) return; | ||
await context.read<TaskListCubit>().addTask(Task( | ||
id: -1, | ||
title: taskData["title"], | ||
description: taskData["description"], | ||
completed: false)); | ||
}, | ||
child: const Icon(Icons.add), | ||
); | ||
}), | ||
body: Padding( | ||
padding: const EdgeInsets.all(24), | ||
child: BlocBuilder<TaskListCubit, TaskListState>( | ||
builder: (context, state) { | ||
return switch (state) { | ||
TaskListInitial() => const SizedBox.shrink(), | ||
TaskListLoading() => const CircularProgressIndicator(), | ||
TaskListLoaded(tasks: final tasks) => ListView.builder( | ||
itemCount: tasks.length, | ||
itemBuilder: (context, index) { | ||
return Dismissible( | ||
background: Container(color: Colors.red), | ||
key: ValueKey(tasks[index].id), | ||
onDismissed: (_) { | ||
context | ||
.read<TaskListCubit>() | ||
.deleteTask(tasks[index].id); | ||
}, | ||
child: CheckboxListTile( | ||
value: tasks[index].completed, | ||
title: Text(tasks[index].title), | ||
subtitle: Text(tasks[index].description), | ||
onChanged: tasks[index].completed | ||
? null | ||
: (value) { | ||
context | ||
.read<TaskListCubit>() | ||
.markTasksAsCompleted( | ||
[tasks[index].id]); | ||
}, | ||
), | ||
); | ||
}, | ||
), | ||
TaskListError(message: final message) => Text(message), | ||
_ => throw Exception("Invalid state"), | ||
}; | ||
}, | ||
), | ||
), | ||
), | ||
), | ||
); | ||
} | ||
} | ||
|
||
class _TaskAdd extends StatefulWidget { | ||
const _TaskAdd(); | ||
|
||
@override | ||
State<_TaskAdd> createState() => __TaskAddState(); | ||
} | ||
|
||
class __TaskAddState extends State<_TaskAdd> { | ||
@override | ||
Widget build(BuildContext context) { | ||
return const Placeholder(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export 'libsql_task_repository.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import 'package:libsql_dart/libsql_dart.dart'; | ||
import 'package:libsql_dart_example/features/task/models/task.dart'; | ||
import 'package:libsql_dart_example/features/task/repositories/repositories.dart'; | ||
|
||
class LibsqlTaskRepository extends TaskRepository { | ||
final LibsqlClient _client; | ||
|
||
LibsqlTaskRepository(this._client); | ||
|
||
@override | ||
Future<void> addTask(Task task) async { | ||
await _client.execute( | ||
"insert into tasks (title, description, completed) values (?, ?, ?)", | ||
positional: [task.title, task.description, task.completed ? 1 : 0]); | ||
} | ||
|
||
@override | ||
Future<void> deleteTask(int id) async { | ||
await _client.execute("delete from tasks where id = ?", positional: [id]); | ||
} | ||
|
||
@override | ||
Future<List<Task>> getTasks() async { | ||
return _client.query("select * from tasks").then((value) => value | ||
.map((row) => Task( | ||
id: row["id"], | ||
title: row["title"], | ||
description: row["description"], | ||
completed: row["completed"] == 1, | ||
)) | ||
.toList()); | ||
} | ||
|
||
@override | ||
Future<void> markTasksAsCompleted(List<int> ids) async { | ||
await _client.execute( | ||
"update tasks set completed = 1 where id in (${ids.join(",")})", | ||
); | ||
} | ||
} |
Oops, something went wrong.