Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/enum support #681

Merged
merged 9 commits into from
Aug 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/entities.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Floor entities can hold values of the following Dart types which map to their co
- `String` - TEXT
- `bool` - INTEGER (0 = false, 1 = true)
- `Uint8List` - BLOB
- `enum` - INTEGER (records by the index 0..n)

In case you want to store sophisticated Dart objects that can be represented by one of the above types, take a look at [Type Converters](type-converters.md).

Expand Down
40 changes: 33 additions & 7 deletions example/lib/database.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

114 changes: 97 additions & 17 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class FloorApp extends StatelessWidget {
}
}

class TasksWidget extends StatelessWidget {
class TasksWidget extends StatefulWidget {
final String title;
final TaskDao dao;

Expand All @@ -42,35 +42,75 @@ class TasksWidget extends StatelessWidget {
required this.dao,
}) : super(key: key);

@override
State<StatefulWidget> createState() => TasksWidgetState();
}

class TasksWidgetState extends State<TasksWidget> {
TaskType? _selectedType;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
appBar: AppBar(
title: Text(widget.title),
actions: <Widget>[
PopupMenuButton<int>(
itemBuilder: (context) {
return List.generate(
TaskType.values.length + 1, //Uses increment to handle All types
(index) {
return PopupMenuItem<int>(
value: index,
child: Text(
index == 0 ? 'All' : _getMenuType(index).title,
),
);
},
);
},
onSelected: (index) {
setState(() {
_selectedType = index == 0 ? null : _getMenuType(index);
});
},
)
],
),
body: SafeArea(
child: Column(
children: <Widget>[
TasksListView(dao: dao),
TasksTextField(dao: dao),
TasksListView(
dao: widget.dao,
selectedType: _selectedType,
),
TasksTextField(dao: widget.dao),
],
),
),
);
}

TaskType _getMenuType(int index) => TaskType.values[index - 1];
}

class TasksListView extends StatelessWidget {
final TaskDao dao;
final TaskType? selectedType;

const TasksListView({
Key? key,
required this.dao,
required this.selectedType,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return Expanded(
child: StreamBuilder<List<Task>>(
stream: dao.findAllTasksAsStream(),
stream: selectedType == null
? dao.findAllTasksAsStream()
: dao.findAllTasksByTypeAsStream(selectedType!),
builder: (_, snapshot) {
if (!snapshot.hasData) return Container();

Expand Down Expand Up @@ -105,20 +145,60 @@ class TaskListCell extends StatelessWidget {
Widget build(BuildContext context) {
return Dismissible(
key: Key('${task.hashCode}'),
background: Container(color: Colors.red),
direction: DismissDirection.endToStart,
background: Container(
padding: const EdgeInsets.only(left: 16),
color: Colors.green,
child: const Align(
child: Text(
'Change status',
style: TextStyle(color: Colors.white),
),
alignment: Alignment.centerLeft,
),
),
secondaryBackground: Container(
padding: const EdgeInsets.only(right: 16),
color: Colors.red,
child: const Align(
child: Text(
'Delete',
style: TextStyle(color: Colors.white),
),
alignment: Alignment.centerRight,
),
),
direction: DismissDirection.horizontal,
child: ListTile(
leading: Text(task.message),
title: Text(task.message),
subtitle: Text('Status: ${task.type.title}'),
dkaera marked this conversation as resolved.
Show resolved Hide resolved
trailing: Text(task.timestamp.toIso8601String()),
),
onDismissed: (_) async {
await dao.deleteTask(task);

final scaffoldMessengerState = ScaffoldMessenger.of(context);
scaffoldMessengerState.hideCurrentSnackBar();
scaffoldMessengerState.showSnackBar(
const SnackBar(content: Text('Removed task')),
);
confirmDismiss: (direction) async {
String? statusMessage;
switch (direction) {
case DismissDirection.endToStart:
await dao.deleteTask(task);
statusMessage = 'Removed task';
break;
case DismissDirection.startToEnd:
final tasksLength = TaskType.values.length;
final nextIndex = (tasksLength + task.type.index + 1) % tasksLength;
final taskCopy = task.copyWith(type: TaskType.values[nextIndex]);
await dao.updateTask(taskCopy);
statusMessage = 'Updated task status by: ${taskCopy.type.title}';
break;
default:
break;
}

if (statusMessage != null) {
final scaffoldMessengerState = ScaffoldMessenger.of(context);
scaffoldMessengerState.hideCurrentSnackBar();
scaffoldMessengerState.showSnackBar(
SnackBar(content: Text(statusMessage)),
);
}
return statusMessage != null;
},
);
}
Expand Down Expand Up @@ -174,7 +254,7 @@ class TasksTextField extends StatelessWidget {
if (message.trim().isEmpty) {
_textEditingController.clear();
} else {
final task = Task(null, message, DateTime.now());
final task = Task.optional(message: message);
await dao.insertTask(task);
_textEditingController.clear();
}
Expand Down
68 changes: 60 additions & 8 deletions example/lib/task.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,65 @@
import 'package:floor/floor.dart';

enum TaskType {
open('Open'),
inProgress('In Progress'),
done('Done');

final String title;

const TaskType(this.title);
}

@entity
class Task {
@PrimaryKey(autoGenerate: true)
final int? id;

final String message;

final bool isRead;

final DateTime timestamp;

Task(this.id, this.message, this.timestamp);
final TaskType type;

Task(this.id, this.isRead, this.message, this.timestamp, this.type);

factory Task.optional({
int? id,
DateTime? timestamp,
String? message,
bool? isRead,
TaskType? type,
}) =>
Task(
id,
isRead ?? false,
message ?? 'empty',
timestamp ?? DateTime.now(),
type ?? TaskType.open,
);

@override
String toString() {
return 'Task{id: $id, message: $message, read: $isRead, timestamp: $timestamp, type: $type}';
}

Task copyWith({
int? id,
String? message,
bool? isRead,
DateTime? timestamp,
TaskType? type,
}) {
return Task(
id ?? this.id,
isRead ?? this.isRead,
message ?? this.message,
timestamp ?? this.timestamp,
type ?? this.type,
);
}

@override
bool operator ==(Object other) =>
Expand All @@ -18,13 +68,15 @@ class Task {
runtimeType == other.runtimeType &&
id == other.id &&
message == other.message &&
timestamp == other.timestamp;
isRead == other.isRead &&
timestamp == other.timestamp &&
type == other.type;

@override
int get hashCode => id.hashCode ^ message.hashCode ^ timestamp.hashCode;

@override
String toString() {
return 'Task{id: $id, message: $message, timestamp: $timestamp}';
}
int get hashCode =>
id.hashCode ^
message.hashCode ^
isRead.hashCode ^
timestamp.hashCode ^
type.hashCode;
}
3 changes: 3 additions & 0 deletions example/lib/task_dao.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ abstract class TaskDao {
@Query('SELECT * FROM task')
Stream<List<Task>> findAllTasksAsStream();

@Query('SELECT * FROM task WHERE type = :type')
Stream<List<Task>> findAllTasksByTypeAsStream(TaskType type);

@insert
Future<void> insertTask(Task task);

Expand Down
Loading