Eelmise aasta keskel tahtsin Androidi rakenduse iOS-i ja veebi teisaldada. Mobiilplatvormide jaoks oli valik lehvimine ja ma mõtlesin, mida valida veebi jaoks.
Kuigi ma armusin Flutterisse esimesest silmapilgust, olid mul siiski mõned reservatsioonid: Olekut levitades vidinapuust, Flutter's InheritedWidget
või Redux - koos kõigi variatsioonidega - teeb selle töö ära, kuid uue raamistiku nagu Flutter puhul võiks eeldada, et vaatekiht oleks veidi reaktiivsem, st vidinad oleksid ise kodakondsuseta ja muutuksid vastavalt nende olekule söödetakse väljastpoolt, aga nad pole seda. Samuti toetab Flutter ainult Androidi ja iOS-i, kuid ma tahtsin veebis avaldada. Mul on rakenduses juba palju äriloogikat ja tahtsin seda võimalikult palju taaskasutada ning idee muuta koodi vähemalt kahes kohas ühe äriloogika muudatuse jaoks oli vastuvõetamatu.
Hakkasin ringi vaatama, kuidas sellest üle saada, ja sattusin BLoC-le. Kiireks sissejuhatuseks soovitan vaadata Flutter / AngularDart - koodi jagamine, parem koos (DartConf 2018) kui teil on aega.
BLoC on uhke sõna, mille leiutas Google ja mis tähendab b kasutegur gic c komponendid. ' BLoC mustri idee on salvestada võimalikult suur osa oma äriloogikast puhtasse Dart-koodi, et seda saaksid teised platvormid uuesti kasutada. Selle saavutamiseks peate järgima reegleid:
edukaim tutvumissait 2016
Viimane asi, mida meeles pidada, on see, et BLoC sisendiks peaks olema valamu , samal ajal kui väljund toimub a kaudu voog . Need on mõlemad osa StreamController
.
Kui järgite veebi- (või mobiilirakenduse!) Rakenduse kirjutamisel rangelt neid reegleid, võib mobiiliversiooni (või veebi!) Loomine olla sama lihtne kui vaadete ja platvormispetsiifiliste liideste loomine. Isegi kui olete just alustanud AngularDarti või Flutteri kasutamist, on põhiplatvormi teadmistega vaadete tegemine siiski lihtne. Võite lõpuks taaskasutada enam kui poole oma koodibaasist. BLoC muster hoiab kõik struktureeritud ja hõlpsasti hooldatav.
Tegin lihtsa kogu rakendus lehes Flutter ja AngularDart. Rakendus kasutab Firecloudi loomise vaatamiseks tagatipuna ja reaktiivse lähenemisena. Rakendusel on kolm osa:
bloc
todo_app_flutter
todoapp_dart_angular
Võite valida, et teil oleks rohkem osi - näiteks andmeliides, lokaliseerimisliides jne. Tuleb meeles pidada, et iga kiht peaks suhtlema teisega liidese kaudu.
Väljal bloc/
kataloog:
lib/src/bloc
: BloC-moodulid on siin salvestatud puhaste Dart-teekidena, mis sisaldavad äriloogikat.lib/src/repository
: Andmeliidesed salvestatakse kataloogi.lib/src/repository/firestore
: Hoidla sisaldab FireCloudi liidest andmetele koos selle mudeliga ja kuna see on näidisrakendus, on meil ainult üks andmemudel todo.dart
ja üks liides andmetele todo_repository.dart
; reaalses rakenduses on mudeleid ja hoidlaliideseid aga rohkem.lib/src/repository/preferences
sisaldab preferences_interface.dart
lihtsat liidest, mis salvestab veebis sisselogitud kasutajanimed edukalt kohalikku salvestusruumi või mobiilseadmete jagatud eelistusi.//BLOC abstract class PreferencesInterface{ //Preferences final DEFAULT_USERNAME = 'DEFAULT_USERNAME'; Future initPreferences(); String get defaultUsername; void setDefaultUsername(String username); }
Veebi- ja mobiilirakendused peavad selle poodi juurutama ja hankima vaikimisi kasutajanime kohalikust salvestusruumist / eelistustest. Selle AngularDart juurutamine näeb välja selline:
// ANGULAR DART class PreferencesInterfaceImpl extends PreferencesInterface { SharedPreferences _prefs; @override Future initPreferences() async => _prefs = await SharedPreferences.getInstance(); @override void setDefaultUsername(String username) => _prefs.setString(DEFAULT_USERNAME, username); @override String get defaultUsername => _prefs.getString(DEFAULT_USERNAME); }
Siin pole midagi tähelepanuväärset - see rakendab vajalikku. Võite märgata initPreferences()
async meetod, mis tagastab null
. See meetod tuleb rakendada lehvimise poolel, kuna SharedPreferences
eksemplar mobiilseadmes on asünkroonne.
ruby on rails rest api õpetus
//FLUTTER @override Future initPreferences() async => _prefs = await SharedPreferences.getInstance();
Püsime natuke lib / src / bloki dir. Igas vaates, mis haldab mõnda äriloogikat, peaks olema oma komponent BLoC. Selles juhendis näete vaateid BLoC base_bloc.dart
, endpoints.dart
ja session.dart
. Viimane vastutab kasutaja sisse- ja väljalogimise ning hoidlaliideste lõpp-punktide pakkumise eest. Sessiooniliidese olemasolu põhjuseks on firebase
ja firecloud
paketid pole veebi ja mobiili jaoks ühesugused ning need tuleb rakendada platvormi põhjal.
// BLOC abstract class Session implements Endpoints { //Collections. @protected final String userCollectionName = 'users'; @protected final String todoCollectionName = 'todos'; String userId; Session(){ _isSignedIn.stream.listen((signedIn) { if(!signedIn) _logout(); }); } final BehaviorSubject _isSignedIn = BehaviorSubject(); Stream get isSignedIn => _isSignedIn.stream; Sink get signedIn => _isSignedIn.sink; Future signIn(String username, String password); @protected void logout(); void _logout() { logout(); userId = null; } }
Idee on hoida sessiooniklass globaalsena (singleton). Selle _isSignedIn.stream
põhjal getter, see haldab rakenduse vahetust sisselogimis- / loendivaate vahel ja pakub lõpp-punkte hoidla rakendustele, kui userId on olemas (st kasutaja on sisse logitud).
base_bloc.dart
on kõigi BLoC-de alus. Selles näites tegeleb see vastavalt vajadusele laadimisindikaatori ja tõrke dialoogi kuvamisega.
Äriloogika näitena vaatame pilti todo_add_edit_bloc.dart
Faili pikk nimi selgitab selle eesmärki. Sellel on privaatne void-meetod _addUpdateTodo(bool addUpdate)
.
// BLOC void _addUpdateTodo(bool addUpdate) { if(!addUpdate) return; //Check required. if(_title.value.isEmpty) _todoError.sink.add(0); else if(_description.value.isEmpty) _todoError.sink.add(1); else _todoError.sink.add(-1); if(_todoError.value >= 0) return; final TodoBloc todoBloc = _todo.value == null ? TodoBloc('', false, DateTime.now(), null, null, null) : _todo.value; todoBloc.title = _title.value; todoBloc.description = _description.value; showProgress.add(true); _toDoRepository.addUpdateToDo(todoBloc) .doOnDone( () => showProgress.add(false) ) .listen((_) => _closeDetail.add(true) , onError: (err) => error.add( err.toString()) ); }
Selle meetodi sisend on bool addUpdate
ja see on final BehaviorSubject _addUpdate = BehaviorSubject()
kuulaja. Kui kasutaja klõpsab rakenduses nupul Salvesta, saadab sündmus selle teema valamu tõelise väärtuse ja käivitab selle funktsiooni BLoC. See tükk lehvivat koodi võlub vaate küljel.
// FLUTTER IconButton(icon: Icon(Icons.done), onPressed: () => _todoAddEditBloc.addUpdateSink.add(true),),
_addUpdateTodo
kontrollib, et nii pealkiri kui ka kirjeldus ei oleks tühjad ja muudaks _todoError
väärtust BehaviorSubject põhineb sellel tingimusel. _todoError
tõrge vastutab sisendväljadel kuvavea kuvamise käivitamise eest, kui väärtust pole sisestatud. Kui kõik on korras, kontrollib see, kas luua TodoBloc
ja lõpuks _toDoRepository
kirjutab FireCloudi.
Äriloogika on siin, kuid pange tähele:
_addUpdateTodo
on privaatne ja sellele ei pääse vaatega juurde._title.value
ja _description.value
täidetakse kasutaja sisestades väärtuse tekstisisestusse. Teksti sisestamine tekstimuutmise sündmusel saadab selle väärtuse vastavatele valamutele. Nii on meil BLoC-s väärtuste reaktiivne muutus ja nende kuvamine vaates._toDoRepository
on platvormist sõltuv ja süstitakse.Vaadake todo_list.dart
koodi BLoC _getTodos()
meetod. See kuulab todokogu hetktõmmist ja voogesitab kogu vaate loendis loetletud andmed. Vaate loend joonistatakse kogu ümber voo muutuse põhjal.
// BLOC void _getTodos(){ showProgress.add(true); _toDoRepository.getToDos() .listen((todosList) { todosSink.add(todosList); showProgress.add(false); }, onError: (err) { showProgress.add(false); error.add(err.toString()); }); }
Voogude või rx ekvivalendi kasutamisel on oluline teada, et ojad peavad olema suletud. Me teeme seda dispose()
-s iga BLoC meetod. Hävitage iga vaate BLoC selle hävitamise / hävitamise meetodis.
milline neist ei aita teil mõista töövoo laiemat ulatust?
// FLUTTER @override void dispose() { widget.baseBloc.dispose(); super.dispose(); }
Või AngularDart projektis:
// ANGULAR DART @override void ngOnDestroy() { todoListBloc.dispose(); }
Me ütlesime varem, et kõik, mis BLoC-s tuleb, peab olema lihtne Dart ja mitte midagi platvormist sõltuvat. TodoAddEditBloc
vajab ToDoRepository
Firestore'i kirjutama. Firebase'il on platvormist sõltuvad paketid ja meil peab olema ToDoRepository
eraldi rakendused liides. Need rakendused süstitakse rakendustesse. Flutteri jaoks kasutasin flutter_simple_dependency_injection
pakett ja see näeb välja selline:
// FLUTTER class Injection { static Firestore _firestore = Firestore.instance; static FirebaseAuth _auth = FirebaseAuth.instance; static PreferencesInterface _preferencesInterface = PreferencesInterfaceImpl(); static Injector injector; static Future initInjection() async { await _preferencesInterface.initPreferences(); injector = Injector.getInjector(); //Session injector.map((i) => SessionImpl(_auth, _firestore), isSingleton: true); //Repository injector.map((i) => ToDoRepositoryImpl(injector.get()), isSingleton: false); //Bloc injector.map((i) => LoginBloc(_preferencesInterface, injector.get()), isSingleton: false); injector.map((i) => TodoListBloc(injector.get(), injector.get()), isSingleton: false); injector.map((i) => TodoAddEditBloc(injector.get()), isSingleton: false); } }
Kasutage seda sellises vidinas:
// FLUTTER TodoAddEditBloc _todoAddEditBloc = Injection.injector.get();
AngularDartil on pakkujate kaudu sisseehitatud süst.
// ANGULAR DART @GenerateInjector([ ClassProvider(PreferencesInterface, useClass: PreferencesInterfaceImpl), ClassProvider(Session, useClass: SessionImpl), ExistingProvider(Endpoints, Session) ])
Ja komponendis:
// ANGULAR DART providers: [ overlayBindings, ClassProvider(ToDoRepository, useClass: ToDoRepositoryImpl), ClassProvider(TodoAddEditBloc), ExistingProvider(BaseBloc, TodoAddEditBloc) ],
Näeme, et Session
on globaalne. See pakub sisse- ja väljalogimise funktsionaalsust ning ToDoRepository
-is kasutatavaid lõpp-punkte ja BLoC-d. ToDoRepository
vajab lõpp-punktide liidest, mis on rakendatud SessionImpl
-s ja nii edasi. Vaade peaks nägema ainult selle BLoC-d ega midagi enamat.
Vaated peaksid olema võimalikult lihtsad. Nad kuvavad ainult BLoC-st pärinevat ja saadavad kasutaja sisendi BLoC-sse. Läheme sellest üle TodoAddEdit
-ga vidin Flutterist ja selle veebiekvivalent TodoDetailComponent
. Neil kuvatakse valitud ülesande pealkiri ja kirjeldus ning kasutaja saab selle üles lisada või uuendada.
Lehvimine:
// FLUTTER _todoAddEditBloc.todoStream.first.then((todo) { _titleController.text = todo.title; _descriptionController.text = todo.description; });
Ja hiljem koodis ...
// FLUTTER StreamBuilder( stream: _todoAddEditBloc.todoErrorStream, builder: (BuildContext context, AsyncSnapshot errorSnapshot) { return TextField( onChanged: (text) => _todoAddEditBloc.titleSink.add(text), decoration: InputDecoration(hintText: Localization.of(context).title, labelText: Localization.of(context).title, errorText: errorSnapshot.data == 0 ? Localization.of(context).titleEmpty : null), controller: _titleController, ); }, ),
StreamBuilder
vidin ehitab end vea korral uuesti üles (midagi pole sisestatud). See juhtub kuulates _todoAddEditBloc.todoErrorStream . _todoAddEditBloc.titleSink
, mis on pealkirjas olev BLoC kraanikauss ja mida värskendatakse kasutaja tekstiväljale teksti sisestamisel.
mis keeles on rubiin kirjutatud
Selle sisendvälja algväärtus (kui on valitud üks todo) täidetakse kuulates _todoAddEditBloc.todoStream
mis hoiab valitud todot või tühja, kui lisame uue todo.
Väärtuse omistamise tekstiväljale teeb selle kontroller _titleController.text = todo.title;
.
Kui kasutaja otsustab todo salvestada, vajutab ta rakenduse ribal kontrollikooni ja käivitab _todoAddEditBloc.addUpdateSink.add(true)
. See kutsub esile _addUpdateTodo(bool addUpdate)
me rääkisime eelmises jaotises BLoC ja teeme kogu äriloogika, et viga lisada, värskendada või kuvada kasutajale tagasi.
Kõik on reaktiivne ja vidina olekut pole vaja käsitleda.
AngularDart kood on veelgi lihtsam. Pärast komponendi BLoC pakkumist pakkujate abil todo_detail.html
failikood on osa andmete kuvamisest ja kasutajate suhtluse BLoC-le saatmisest.
// AngularDart {{saveStr}}
Sarnaselt Flutterile määrame ka ngModel=
pealkirjavoo väärtus, mis on selle algväärtus.
// AngularDart (inputKeyPress)='todoAddEditBloc.descriptionSink.add($event)'
inputKeyPress
väljundsündmus saadab tekstisisestuses kasutaja kirjutatud märgid BLoC kirjeldusse tagasi. Materjali nupp (trigger)='todoAddEditBloc.addUpdateSink.add(true)'
sündmus saadab BLoC add / update sündmuse, mis taas käivitab sama _addUpdateTodo(bool addUpdate)
funktsioon BLoC-s. Kui vaatate pilti todo_detail.dart
komponendi kood, näete, et seal pole peaaegu midagi peale vaates kuvatavate stringide. Paigutasin need sinna ja mitte HTML-i võimaliku lokaliseerimise tõttu, mida saab siin teha.
Sama kehtib kõigi teiste komponentide kohta - komponentidel ja vidinatel on äriloogika null.
Märkimist väärib veel üks stsenaarium. Kujutage ette, et teil on vaade, millel on keeruline andmete esitamise loogika või midagi sellist nagu tabel väärtusega, mis tuleb vormindada (kuupäevad, valuutad jne). Kellelgi võib tekkida kiusatus hankida BLoC-st väärtused ja need vaates vormindada. See on vale! Vaates kuvatavad väärtused peaksid tulema vaates juba vormindatud (stringid). Selle põhjuseks on see, et vormindamine ise on ka äriloogika. Veel üks näide on see, kui kuva väärtuse vormindamine sõltub mõnest rakenduse parameetrist, mida saab tööajal muuta. Pakkudes selle parameetri BLoC-le ja kasutades kuvareaktsiooni reaktiivset lähenemist, vormindab äriloogika väärtuse ja joonistab ümber ainult vajalikud osad. Selles näites olev BLoC mudel TodoBloc
on väga lihtne. Teisendamine FireCloudi mudelist BLoC mudeliks toimub hoidlas, kuid vajadusel saab seda teha BLoC-s, nii et mudeli väärtused oleksid kuvamiseks valmis.
See lühike artikkel käsitleb BLoC mustri juurutamise põhimõisteid. See on töökindel tõend selle kohta, et koodi jagamine Flutteri ja AngularDarti vahel on võimalik, võimaldades emakeelt ja platvormidevaheline arendus .
Näidet uurides näete, et õigesti rakendatuna lühendab BLoC oluliselt mobiili- / veebirakenduste loomise aega. Näiteks on ToDoRepository
ja selle rakendamine. Rakenduskood on peaaegu identne ja isegi vaate koostamise loogika on sarnane. Pärast paari vidinat / komponenti saate kiiresti alustada masstootmist.
Loodan, et see artikkel heidab teile pilgu sellest lõbususest ja entusiasmist, mida olen teinud veebi- / mobiilirakenduste jaoks, kasutades Flutter / AngularDart ja BLoC mustrit. Kui soovite luua JavaScripti platvormidevahelisi töölauarakendusi, lugege Electron: Platvormidevahelised töölauarakendused on lihtsaks tehtud autor ApeeScapeer Stéphane P. Péricat.
Seotud: Noolemängu keel: kui Java ja C # pole piisavalt teravadFlutter on mobiilne (Android / iOS) arendusplatvorm. See on keskendunud kohalikule kõrgekvaliteedilisele kasutajakogemusele ja rikkaliku kasutajaliidese rakenduste kiirele arendamisele.
Flutter kasutab Google'i väljatöötatud kaasaegset, ülevaatlikku ja objektorienteeritud keelt Dart.
AngularDart on Dart-nurga port. Dart-kood on kompileeritud JavaScripti.
Kompilaator toetab IE11, Chrome'i, Edge'i, Firefoxi ja Safarit.
'Business Logic Component' ehk BLoC on arengumuster. BLoC-i idee on isoleerida võimalikult palju äriloogikat puhtas Dart-koodis. See annab koodibaasi, mida saab jagada mobiil- ja veebiplatvormide vahel.
e-kaubanduse veebisaidi kujundus
BLoC jaoks loodud rakendused peavad olema kihilised. Vaate kiht, äriloogika kiht, andmetele juurdepääsukiht jne tuleb eraldada. Iga kiht suhtleb liideste kaudu allpool asuva kihiga. Liides peab olema puhas Dart-kood ja liidese rakendamine võib olla platvormispetsiifiline (Flutter või AngularDart).
Neid on neli: BLoC sisendid ja väljundid peaksid olema ainult voogud ja valamud, sõltuvused peavad olema süstitavad ja platvormi agnostilised, äriloogikal ei tohiks olla platvormi hargnemist ja saate hallata vaate olekut või kasutada reaktiivset lähenemist.
BLoC muster ei hooli vaatest ja sellest, kuidas see kasutaja kuvamist / suhtlemist käsitleb. Kuid kuna see kasutab väljundite ja sisenditena ainult vooge ja valamuid, on see ideaalselt kohandatud vaate poolel reaktiivseks lähenemiseks.
Flutter erineb enamikust teistest lahendustest selle poolest, et kasutab vidinate joonistamiseks oma suure jõudlusega renderdamismootorit.