Skip to content

Commit

Permalink
Merge branch 'obx-953-sync-demo' into 'main'
Browse files Browse the repository at this point in the history
Update Sync demo to match Java/C++

See merge request objectbox/objectbox-dart!56
  • Loading branch information
greenrobot-team committed Oct 10, 2023
2 parents e220ac9 + cde828a commit 792e238
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 69 deletions.
19 changes: 17 additions & 2 deletions objectbox/example/flutter/objectbox_demo_sync/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
# objectbox_demo
# objectbox_demo_sync

## Getting Started with ObjectBox Sync in Flutter / Dart

This project contains the Flutter version of the main example from the [objectbox-examples](https://github.com/objectbox/objectbox-examples) repository.
This project contains the Flutter version of the Sync example from the [objectbox-examples](https://github.com/objectbox/objectbox-examples) repository.

You need to have a Sync Trial and run the Sync Server to run the demo --> Apply for the [Sync Trial here](https://objectbox.io/sync/).

Also, do check out the [Sync Docs](https://sync.objectbox.io/).

The basic steps to get this demo running (assuming you have a working Flutter setup):

```
# Set up project, get dependencies
flutter pub get
# Generate model and code files for ObjectBox
flutter pub run build_runner build
# Run the app in debug mode
flutter run
```

Optional: depending on your setup you might have to adjust the Sync server address in [objectbox.dart](/lib/objectbox.dart).
102 changes: 62 additions & 40 deletions objectbox/example/flutter/objectbox_demo_sync/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,55 +41,77 @@ class MyHomePage extends StatefulWidget {
}

class _MyHomePageState extends State<MyHomePage> {
final _noteInputController = TextEditingController();
final _textInputController = TextEditingController();

Future<void> _addNote() async {
if (_noteInputController.text.isEmpty) return;
await objectbox.addNote(_noteInputController.text);
_noteInputController.text = '';
Future<void> _addTask() async {
if (_textInputController.text.isEmpty) return;
await objectbox.addTask(_textInputController.text);
_textInputController.text = '';
}

@override
void dispose() {
_noteInputController.dispose();
_textInputController.dispose();
super.dispose();
}

GestureDetector Function(BuildContext, int) _itemBuilder(List<Note> notes) =>
(BuildContext context, int index) => GestureDetector(
onTap: () => objectbox.removeNote(notes[index].id),
Widget Function(BuildContext, int) _itemBuilder(List<Task> tasks) =>
(BuildContext context, int index) => Dismissible(
background: Container(color: Colors.red),
key: UniqueKey(),
onDismissed: (direction) {
objectbox.removeTask(tasks[index].id);
// List updated via watched query stream.
},
child: Row(
children: <Widget>[
Expanded(
child: Container(
// draw bottom border
decoration: const BoxDecoration(
border:
Border(bottom: BorderSide(color: Colors.black12))),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 18.0, horizontal: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
notes[index].text,
style: const TextStyle(
fontSize: 15.0,
),
// Provide a Key for the integration test
key: Key('list_item_$index'),
),
Padding(
padding: const EdgeInsets.only(top: 5.0),
child: Text(
'Added on ${notes[index].dateFormat}',
style: const TextStyle(
fontSize: 12.0,
padding: const EdgeInsets.symmetric(
vertical: 18.0, horizontal: 10.0),
child: Row(
children: [
Checkbox(
value: tasks[index].isFinished(),
onChanged: (bool? value) {
// not tri-state, so value is never null
objectbox.changeTaskFinished(
tasks[index], value!);
// List updated via watched query stream.
}),
Container(
padding: const EdgeInsets.only(left: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
tasks[index].text,
// strike-through text style for finished tasks
style: tasks[index].isFinished()
? const TextStyle(
color: Colors.grey,
decoration: TextDecoration.lineThrough)
: const TextStyle(fontSize: 15.0),
// Provide a Key for the integration test
key: Key('list_item_$index'),
),
),
Padding(
padding: const EdgeInsets.only(top: 5.0),
child: Text(
tasks[index].getStateText(),
style: const TextStyle(
fontSize: 12.0,
),
),
),
],
),
],
),
),
],
),
),
),
Expand All @@ -113,10 +135,10 @@ class _MyHomePageState extends State<MyHomePage> {
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: TextField(
decoration: const InputDecoration(
hintText: 'Enter a new note'),
controller: _noteInputController,
onSubmitted: (value) => _addNote(),
decoration:
const InputDecoration(hintText: 'Enter new task'),
controller: _textInputController,
onSubmitted: (value) => _addTask(),
// Provide a Key for the integration test
key: const Key('input'),
),
Expand All @@ -126,7 +148,7 @@ class _MyHomePageState extends State<MyHomePage> {
child: Align(
alignment: Alignment.centerRight,
child: Text(
'Tap a note to remove it',
'Delete a task by swiping it',
style: TextStyle(
fontSize: 11.0,
color: Colors.grey,
Expand All @@ -141,8 +163,8 @@ class _MyHomePageState extends State<MyHomePage> {
),
),
Expanded(
child: StreamBuilder<List<Note>>(
stream: objectbox.getNotes(),
child: StreamBuilder<List<Task>>(
stream: objectbox.getTasks(),
builder: (context, snapshot) => ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.symmetric(horizontal: 20.0),
Expand All @@ -154,7 +176,7 @@ class _MyHomePageState extends State<MyHomePage> {
// See https://github.com/flutter/flutter/issues/9383
floatingActionButton: FloatingActionButton(
key: const Key('submit'),
onPressed: _addNote,
onPressed: _addTask,
child: const Icon(Icons.add),
),
);
Expand Down
48 changes: 41 additions & 7 deletions objectbox/example/flutter/objectbox_demo_sync/lib/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,51 @@ import 'objectbox.g.dart';

@Entity()
@Sync()
class Note {
class Task {
int id;

String text;
String? comment;

/// Note: Stored in milliseconds without time zone info.
DateTime date;
/// Note: DateTime is stored in milliseconds without time zone info.
@Property(type: PropertyType.date)
DateTime dateCreated;

Note(this.text, {this.id = 0, this.comment, DateTime? date})
: date = date ?? DateTime.now();
@Property(type: PropertyType.date)
DateTime dateFinished;

String get dateFormat => DateFormat('dd.MM.yyyy hh:mm:ss').format(date);
/// Create task with the given text at the current time.
Task(this.text, {this.id = 0, DateTime? dateCreated, DateTime? dateFinished})
: dateCreated = dateCreated ?? DateTime.now(),
dateFinished = dateFinished ?? DateTime.fromMicrosecondsSinceEpoch(0);

bool isFinished() {
return dateFinished.millisecondsSinceEpoch != 0;
}

void setIsFinished(bool isFinished) {
if (isFinished) {
dateFinished = DateTime.now();
} else {
dateFinished = DateTime.fromMicrosecondsSinceEpoch(0);
}
}

String get dateCreatedFormat =>
DateFormat('dd.MM.yy HH:mm:ss').format(dateCreated);

String get dateFinishedFormat =>
DateFormat('dd.MM.yy HH:mm:ss').format(dateFinished);

/// If the task is new returns 'Created on <date>',
/// if it is finished 'Finished on <date>'. The date is formatted
/// for the current locale.
String getStateText() {
String text;
if (isFinished()) {
text = 'Finished on $dateFinishedFormat';
} else {
text = 'Created on $dateCreatedFormat';
}
return text;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,37 @@
"_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
"entities": [
{
"id": "1:2802681814019499133",
"lastPropertyId": "4:6451339597165131221",
"name": "Note",
"id": "1:6645479796472661392",
"lastPropertyId": "5:6240065879507520219",
"name": "Task",
"flags": 2,
"properties": [
{
"id": "1:3178873177797362769",
"id": "1:9211738071025439652",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:4285343053028527696",
"id": "2:8804670454579230281",
"name": "text",
"type": 9
},
{
"id": "3:2606273611209948020",
"name": "comment",
"type": 9
"id": "4:1260602348787983453",
"name": "dateCreated",
"type": 10
},
{
"id": "4:6451339597165131221",
"name": "date",
"id": "5:6240065879507520219",
"name": "dateFinished",
"type": 10
}
],
"relations": []
}
],
"lastEntityId": "1:2802681814019499133",
"lastEntityId": "1:6645479796472661392",
"lastIndexId": "0:0",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",
Expand Down
24 changes: 15 additions & 9 deletions objectbox/example/flutter/objectbox_demo_sync/lib/objectbox.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ class ObjectBox {
/// The Store of this app.
late final Store _store;

/// A Box of notes.
late final Box<Note> _noteBox;
/// A Box of tasks.
late final Box<Task> _taskBox;

ObjectBox._create(this._store) {
_noteBox = Box<Note>(_store);
_taskBox = _store.box();

// TODO configure actual sync server address and authentication
// For configuration and docs, see objectbox/lib/src/sync.dart
Expand All @@ -44,10 +44,11 @@ class ObjectBox {
return ObjectBox._create(store);
}

Stream<List<Note>> getNotes() {
// Query for all notes, sorted by their date.
Stream<List<Task>> getTasks() {
// Query for all tasks, sorted by their date.
// https://docs.objectbox.io/queries
final builder = _noteBox.query().order(Note_.date, flags: Order.descending);
final builder =
_taskBox.query().order(Task_.dateCreated, flags: Order.descending);
// Build and watch the query,
// set triggerImmediately to emit the query immediately on listen.
return builder
Expand All @@ -56,13 +57,18 @@ class ObjectBox {
.map((query) => query.find());
}

/// Add a note.
/// Add a task.
///
/// To avoid frame drops, run ObjectBox operations that take longer than a
/// few milliseconds, e.g. putting many objects, asynchronously.
/// For this example only a single object is put which would also be fine if
/// done using [Box.put].
Future<void> addNote(String text) => _noteBox.putAsync(Note(text));
Future<void> addTask(String text) => _taskBox.putAsync(Task(text));

Future<void> removeNote(int id) => _noteBox.removeAsync(id);
Future<void> removeTask(int id) => _taskBox.removeAsync(id);

Future<void> changeTaskFinished(Task task, bool isFinished) {
task.setIsFinished(isFinished);
return _taskBox.putAsync(task);
}
}

0 comments on commit 792e238

Please sign in to comment.