Skip to content

Commit

Permalink
wasm example todo app (#5)
Browse files Browse the repository at this point in the history
* wasm: todo cubit

- passing JSON serialized filtered todos
- todo cubit remove expiry from menu as wasm doesn't support threads
  easily
- added web support

* chore: optimize wasm release build

- remove threading code

* wasm: adding generated files need to run app

* chore: using published wasmjsgen

* chore: remove support for non-web architectures

* chore: fixing wasm related links in comments
  • Loading branch information
thlorenz committed Aug 4, 2021
1 parent cd3bf52 commit afa852d
Show file tree
Hide file tree
Showing 47 changed files with 4,032 additions and 0 deletions.
80 changes: 80 additions & 0 deletions flutter/todo_cubit_wasm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
.DS_Store
.idea/
.metadata

## Flutter ###
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.fvm/
.packages
.pub-cache/
.pub/
build/
coverage/
lib/generated_plugin_registrant.dart
# For library packages, don’t commit the pubspec.lock file.
# Regenerating the pubspec.lock file lets you test your package against the latest compatible versions of its dependencies.
# See https://dart.dev/guides/libraries/private-files#pubspeclock
#pubspec.lock

# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/key.properties
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java

# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/.last_build_id
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*

# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages

### Rust ###
**/target/**
**/target_wasm/**
**/Cargo.lock

### Rid ###
**/generated/**
**/Classes/bindings.h
**/macos/*.a
**/ios/*.a
**/android/src/main/jniLibs/*

23 changes: 23 additions & 0 deletions flutter/todo_cubit_wasm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "todo_cubit"
version = "0.1.0"
authors = ["Thorsten Lorenz <thlorenz@gmx.de>"]
edition = "2018"

[lib]
crate-type = ["cdylib", "staticlib" ]

[[bin]]
name = "rid_build"
path = "rid_build.rs"

[dependencies]
cbindgen = "0.18.0"
rid_build = { path = "../../../rid/rid-build" }
rid = { path = "../../../rid" }
serde = "1.0.123"
serde_json = "1.0.64"

# https://rustwasm.github.io/docs/book/reference/code-size.html
[profile.release]
opt-level = 'z'
92 changes: 92 additions & 0 deletions flutter/todo_cubit_wasm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# todo_cubit

Rust integrated Dart Flutter Project

## Getting Started

Use the below scripts to get the app ready to run with Flutter.

### 1. Generate Glue Code

```sh
./sh/bindgen
```

### 2. Build For Desired Target/Device

Run any of the below three to build the binary for the specific device and have it placed into
the devices specific plugin folder.

```sh
./sh/macos
```

### 3. Run with Flutter

Run on the device.

```sh
flutter run -d macos
```

### 4. Develop

Run step `1` whenever a function exposed to Flutter changes.

Run step `2` whenever any of your Rust code changes.

**Note** that to apply changes from Rust you need to restart the app to reload the compiled binary.
A hot restart/reload does not achieve this.

## Folder Structure

```
├── android
├── ios
├── macos
├── lib
├── plugin
│ ├── android
│ ├── ios
│ ├── macos
│ └── lib
└── src
```

### `./plugin`

Provides connection from Flutter to Rust.

Rust binaries are placed into the respective plugin folders `./ios, ./macos, ./android` when
they are built.

Generated Dart glue code is placed inside `./plugin/lib/generated` while
`./plugin/lib/plugin.dart` just exposes the API to the app.

### `./src`

Contains the starter Rust code inside `./src/lib.rs`. Keep developing the Rust part of your app
here.

### `./lib`

Contains the starter Flutter app inside `./lib/main.dart`.

### `./sh`

Provides scripts to run build and code generation tasks. In the future a tool will provide the
functionality currently provided by these scripts.

- `bindgen` generates the `binding.h` header file for the extern Rust functions found inside
`./src`. These are then placed inside the `./plugin` device folders were needed as well as
`./plugin/lib/generated/binding.h` where they are used to generate Dart glue code
- as part of this script `ffigen` generates Dart glue code inside
`./plugin/lib/generated/ffigen_binding.dart` using `./plugin/lib/generated/binding.h` as input
- `./android` builds the Rust binary to run on Android devices/emulators and places it inside
`./plugin/lib/android`
- `./ios` builds the Rust binary to run on IOS devices/emulators and places it inside
`./plugin/lib/ios`
- `./macos` builds the Rust binary to run on MacOs directly and places it inside
`./plugin/lib/macos`, this is the same format as running `cargo build` on your Mac
- `clean` cleans both the Flutter plugin and application, run this to reset Flutter when things
aren't working
29 changes: 29 additions & 0 deletions flutter/todo_cubit_wasm/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml

linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
1 change: 1 addition & 0 deletions flutter/todo_cubit_wasm/assets
12 changes: 12 additions & 0 deletions flutter/todo_cubit_wasm/lib/blocs/cubit/filter_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'package:bloc/bloc.dart';
import 'package:plugin/generated/rid_api.dart';

class FilterCubit extends Cubit<Filter> {
final Store _store = Store.instance;
FilterCubit() : super(Store.instance.filter);

Future<void> setFilter(Filter filter) async {
await _store.msgSetFilter(filter);
emit(_store.filter);
}
}
12 changes: 12 additions & 0 deletions flutter/todo_cubit_wasm/lib/blocs/cubit/settings_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'package:bloc/bloc.dart';
import 'package:plugin/generated/rid_api.dart';

class SettingsCubit extends Cubit<Settings> {
final Store _store = Store.instance;
SettingsCubit() : super(Store.instance.settings);

Future<void> setAutoExpireCompleted(bool val) async {
await _store.msgSetAutoExpireCompletedTodos(val);
emit(_store.settings);
}
}
56 changes: 56 additions & 0 deletions flutter/todo_cubit_wasm/lib/blocs/cubit/todo_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:flutter/foundation.dart';
import 'package:meta/meta.dart';
import 'package:plugin/generated/rid_api.dart';

part 'todo_state.dart';

class TodoCubit extends Cubit<TodoState> {
final Store _store = Store.instance;
late final StreamSubscription<PostedReply> tickSub;
TodoCubit(Todo todo) : super(ExistingTodo(todo)) {
_subscribe();
}

// Reply.Tick is a reply that includes data, in this case the id
// of the completed todo whose life is ticking away
bool _tickIsForThisTodo(PostedReply reply) {
// We make sure that the data is a parseable int id
assert(
reply.data != null,
'Reply.Tick should include data containing the id of the ticked todo',
);
final id = int.tryParse(reply.data!);
assert(id != null, 'Reply.Tick included invalid id ${reply.data}');
return id == state.id;
}

void _subscribe() {
tickSub = replyChannel.stream
.where((x) => x.type == Reply.Tick && _tickIsForThisTodo(x))
.listen(_refreshState);
}

@override
Future<void> close() {
tickSub.cancel();
return super.close();
}

void _refreshState(PostedReply _reply) async {
final todo = _store.todoById(state.id);
if (todo == null) {
emit(MissingTodo(state.id));
} else {
emit(ExistingTodo(todo));
}
}

Future<void> toggleCompleted() =>
_store.msgToggleTodo(state.id).then(_refreshState);

Future<void> removeTodo(int id) =>
_store.msgRemoveTodo(id).then(_refreshState);
}
39 changes: 39 additions & 0 deletions flutter/todo_cubit_wasm/lib/blocs/cubit/todo_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
part of 'todo_cubit.dart';

@immutable
abstract class TodoState {
final int id;

TodoState(this.id);

@override
bool operator ==(Object other) =>
identical(this, other) || other is TodoState && other.id == id;

@override
int get hashCode => id;
}

class ExistingTodo extends TodoState {
final Todo todo;

ExistingTodo(this.todo) : super(todo.id);

@override
bool operator ==(Object other) =>
identical(this, other) || other is ExistingTodo && other.todo == todo;

@override
int get hashCode => todo.hashCode;
}

class MissingTodo extends TodoState {
MissingTodo(int id) : super(id);

@override
bool operator ==(Object other) =>
identical(this, other) || other is MissingTodo && other.id == id;

@override
int get hashCode => super.hashCode;
}

0 comments on commit afa852d

Please sign in to comment.