A ClojureDart framework, inspired by re-frame, for building user interfaces with Flutter
Many (not all) of the concepts in re-frame has been made available in this library, and the aim is to share the same api as far as possible, so if you're familiar with re-frame, picking up re-dash should feel natural.
To gain an understanding of the concepts in re-dash, head over to the excellent documentation in re-frame
Also see State management in ClojureDart by Etienne Théodore for a detailed walk-through
Follow the ClojureDart Quickstart guide to get your app up and running
Then, add the re-dash
dependency
:deps {net.clojars.htihospitality/re-dash {:mvn/version "0.3.0"}}
:deps {hti/re-dash
{:git/url "https://github.com/htihospitality/re-dash.git"
:sha "find the latest sha on github"}}
samples/counter
Shows an example of an incrementing counter when clickedsamples/fetch
Shows an example of data fetching from an http endpoint, using effectssamples/signals
Same as 'samples/counter' but showing various subscription signals: single, vector & mapsamples/coeffects
Shows an example of injecting coeffects into an event handlerWhat follows is an example of the six dominoes principle implemented with re-dash
The full working example is available under samples/counter
(ns acme.view
(:require ["package:flutter/material.dart" :as m]
[acme.model :as model]
[hti.re-dash :as rd]))
...
(m/IconButton
.icon (m/Icon (m/Icons.add_circle_outline))
.onPressed #(rd/dispatch [::model/count])) ;; <== This
...
Both reg-event-db
and reg-event-fx
are supported
(ns acme.model
(:require [hti.re-dash :as rd]))
...
(rd/reg-event-fx
::count
(fn [{:keys [db]} _]
(let [current-count ((fnil inc 0) (:current-count db))]
{:db (assoc db :current-count current-count)
::log-count current-count})))
...
(ns acme.model
(:require [hti.re-dash :as rd]))
...
(rd/reg-fx
::log-count
(fn [current-count]
(println (str "The current-count is " current-count))))
...
Built-in effects: :db
:fx
:dispatch
diapatch-later
:deregister-event-handler
Tip: Need to fetch some data? Do it here then dispatch a new event passing the response.
Subscribe to derived state, internally using ClojureDart Cells (see the Cheatsheet)
(ns acme.view
(:require ["package:flutter/material.dart" :as m]
[acme.model :as model]
[hti.re-dash :as rd]))
...
(f/widget
:watch [current-count (rd/subscribe [::model/get-count])] ;; <== This
(m/Text (str current-count)))
...
This example assumes a subscription called get-count
has been pre-registered in the model.
samples/counter
for a full example of registering the subscription.samples/signals
for more examples of registering subscriptions using extractors and/or signals.Pure. No reference to global state.
(ns acme.view
(:require ["package:flutter/material.dart" :as m]
[cljd.flutter :as f]
[acme.model :as model]
[hti.re-dash :as rd]))
(def counter
(m/Column
.mainAxisAlignment m/MainAxisAlignment.center
.children
[(f/widget
:watch [current-count (rd/subscribe [::model/get-count])]
(m/Text (str current-count)))
(m/IconButton
.icon (m/Icon (m/Icons.add_circle_outline))
.onPressed #(rd/dispatch [::model/count]))
(m/Text "Click me to count!")]))
[More info](http://day8.github.io/re-frame/dominoes-30k/#domino-5-view
Note, this is a contrived example to illustrate usage of this library. Best practice for when state remains local to the widget (for example key presses in a text field) should be handled in a local atom for example:
(ns acme.view
(:require ["package:flutter/material.dart" :as m]
[cljd.flutter :as f]
[acme.model :as model]
[hti.re-dash :as rd]))
(def counter
(f/widget
:watch [current-count (atom 0)]
(m/Column
.mainAxisAlignment m/MainAxisAlignment.center
.children
[(m/Text (str current-count))
(m/IconButton
.icon (m/Icon (m/Icons.add_circle_outline))
.onPressed #(swap! current-count inc))
(m/Text "Click me to count!")])))
Done.
Unfortunately, due to the Dart compiler's tree shaking of unused
code, it incorrectly removes events, effects & subscriptions if declared at the root of a ClojureDart name space. To work around this, we need to wrap all the registrations inside a function callable from main
so the Dart compiler sees there is a reference to the code
(ns acme.main
(:require ["package:flutter/material.dart" :as m]
[cljd.flutter :as f]
[acme.view :as view]
[acme.model :as model]))
(defn main []
(model/register!) ;; <== This
(f/run
(m/MaterialApp
.title "Welcome to Flutter"
.theme (m/ThemeData .primarySwatch m.Colors/blue))
.home
(m/Scaffold
.appBar (m/AppBar
.title (m/Text "ClojureDart with a splash of re-dash")))
.body
m/Center
view/counter))
and in the model
(ns acme.model
(:require [hti.re-dash :as rd]))
(defn register! ;; <== This
[]
(rd/reg-sub
::get-count
(fn [db _]
(:current-count db)))
(rd/reg-fx
::log-count
(fn [current-count]
(println (str "The current-count is " current-count)))))
This does come with a drawback, as whenever we make a change in the model
name space, hot reload does not pick up the changes, so a hot restart is needed instead. Note this only affect our model
name space, hot reload works fine in our view. Maybe there is a way to keep our event registrations from being tree shaken, if so, we'd love to hear it!
Please feel free to raise issues on Github or send pull requests
This section is a guide to developing this re-dash library itself
Clojure and Flutter installed and on your path
Fork, then clone this repository to a local folder like ~/src/re-dash
If you don't already have a ClojureDart/re-dash project you can copy one of the sample projects to start hacking:
cp -r ~/src/re-dash/samples/counter ~/src/counter
Add the re-dash src folder and remove the re-dash dependency from ~/src/counter/deps.edn
{:paths ["src" "../re-dash/src"]
:deps {tensegritics/clojuredart
{:git/url "https://github.com/tensegritics/ClojureDart.git"
:sha "some sha"}}
:aliases {:cljd {:main-opts ["-m" "cljd.build"]}}
:cljd/opts {:kind :flutter
:main acme.main}}
Create the platform folders and run the counter app
cd ~/src/counter
flutter create .
clj -M:cljd flutter -d linux ;; or whichever device you want to run on
Now any changes you make in the re-dash source code will be picked up in the running counter app when doing a hot restart with 'R' in the terminal
re-dash tests are run like this
cd ~/src/re-dash
## first compile the test namespace
clj -M:cljd:test compile hti.re-dash-test
## run the tests
flutter test
Copyright (c) 2023 Hospitality Technology Limited
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close