基于Reacthooks实现的Flutterhooks。Flutterhooks用于管理FlutterWidgeet。有利于增加小部件之间的代码共享,可以代替StatefulWidget。
FlutterHooksAflutterimplemetatioofReacthooks:https://medium.com/@da_abramov/makig-sese-of-react-hooks-fdbde8803889
HooksareaewkidofobjectthatmaagesaWidgetlife-cycles.Theyexistforoereaso:icreasethecodesharigbetweewidgetsadasacompletereplacemetforStatefulWidget.
MotivatioStatefulWidgetsufferfromabigproblem:itisverydifficulttoreusethelogicofsayiitStateordispose.AobviousexampleisAimatioCotroller:
class Example exteds StatefulWidget { fial Duratio duratio; cost Example({Key key, @required this.duratio}) : assert(duratio != ull), super(key: key); @override _ExampleState createState() => _ExampleState();}class _ExampleState exteds State<Example> with SigleTickerProviderStateMixi { AimatioCotroller _cotroller; @override void iitState() { super.iitState(); _cotroller = AimatioCotroller(vsyc: this, duratio: widget.duratio); } @override void didUpdateWidget(Example oldWidget) { super.didUpdateWidget(oldWidget); if (widget.duratio != oldWidget.duratio) { _cotroller.duratio = widget.duratio; } } @override void dispose() { super.dispose(); _cotroller.dispose(); } @override Widget build(BuildCotext cotext) { retur Cotaier(); }}AllwidgetsthatdesiretouseaAimatioCotrollerwillhavetoreimplemetalmostofallthisfromscratch,whichisofcourseudesired.
Dartmixiscapartiallysolvethisissue,buttheysufferfromotherproblems:
Agivemixicaolybeusedoceperclass.
Mixisadtheclasssharesthesameobject.Thismeasthatiftwomixisdefieavariableuderthesameame,theedresultmayvarybetweecompilatiofailtoukowbehavior.
Thislibraryproposeathirdsolutio:
class Example exteds HookWidget { fial Duratio duratio; cost Example({Key key, @required this.duratio}) : assert(duratio != ull), super(key: key); @override Widget build(BuildCotext cotext) { fial cotroller = useAimatioCotroller(duratio: duratio); retur Cotaier(); }}Thiscodeisstrictlyequivalettothepreviousexample.ItstilldisposestheAimatioCotrolleradstillupdatesitsduratiowheExample.duratiochages.Butyou'reprobablythikig:
Wheredidallthelogicgo?
ThatlogicmoveditouseAimatioCotroller,afuctioicludeddirectlyithislibrary(seehttps://github.com/rrousselGit/flutter_hooks#existig-hooks).ItiswhatwecallaHook.
Hooksareaewkidofobjectswithsomespecificities:
TheycaolybeusedithebuildmethodofaHookWidget.
ThesamehookisreusableaifiiteumberoftimesThefollowigcodedefiestwoidepedetAimatioCotroller,adtheyarecorrectlypreservedwhethewidgetrebuild.
Widget build(BuildCotext cotext) { fial cotroller = useAimatioCotroller(); fial cotroller2 = useAimatioCotroller(); retur Cotaier();}Hooksareetirelyidepedetofeachotheradfromthewidget.Whichmeastheycaeasilybeextracteditoapackageadpublishedopubforotherstouse.
PricipleSimilarilytoState,hooksarestoredotheElemetofaWidget.ButisteadofhavigoeState,theElemetstoresaList<Hook>.ThetouseaHook,oemustcallHook.use.
Thehookreturedbyuseisbasedotheumberoftimesithasbeecalled.Thefirstcallretursthefirsthook;thesecodcallretursthesecodhook,thethirdretursthethirdhook,...
Ifthisisstilluclear,aaiveimplemetatioofhooksisthefollowig:
class HookElemet exteds Elemet { List<HookState> _hooks; it _hookIdex; T use<T>(Hook<T> hook) => _hooks[_hookIdex++].build(this); @override performRebuild() { _hookIdex = 0; super.performRebuild(); }}Formoreexplaatioofhowtheyareimplemeted,here'sagreatarticleabouthowtheydiditiReact:https://medium.com/@ryardley/react-hooks-ot-magic-just-arrays-cd4f1857236e
RulesDuetohooksbeigobtaiedfromtheiridex,therearesomerulesthatmustberespected:
DOcalluseucoditioallyWidget build(BuildCotext cotext) { Hook.use(MyHook()); // ....}DON'TwrapuseitoacoditioWidget build(BuildCotext cotext) { if (coditio) { Hook.use(MyHook()); } // ....}DOalwayscallallthehooks:Widget build(BuildCotext cotext) { Hook.use(Hook1()); Hook.use(Hook2()); // ....}DON'Tabortsbuildmethodbeforeallhookshavebeecalled:Widget build(BuildCotext cotext) { Hook.use(Hook1()); if (coditio) { retur Cotaier(); } Hook.use(Hook2()); // ....}Abouthot-reloadSicehooksareobtaiedfromtheiridex,oemaythikthathot-reloadwhilerefactorigwillbreaktheapplicatio.
Butworryot,HookWidgetoverridesthedefaulthot-reloadbehaviortoworkwithhooks.Still,therearesomesituatiosiwhichthestateofaHookmaygetreset.
Cosiderthefollowiglistofhooks:
Hook.use(HookA());Hook.use(HookB(0));Hook.use(HookC(0));Thecosiderthatafterahot-reload,weeditedtheparameterofHookB:
Hook.use(HookA());Hook.use(HookB(42));Hook.use(HookC());Hereeverythigworksfie;allhookskeeptheirstates.
NowcosiderthatweremovedHookB.Weowhave:
Hook.use(HookA());Hook.use(HookC());Ithissituatio,HookAkeepsitsstatebutHookCgetsahardreset.Thishappesbecausewhearefactorigisdoe,allhooksafterthefirstlieimpactedaredisposed.SiceHookCwasplacedafterHookB,isgotdisposed.
HowtouseTherearetwowaystocreateahook:
Afuctio
Fuctiosisbyfarthemostcommowaytowriteahook.Thakstohooksbeigcomposablebyature,afuctiowillbeabletocombieotherhookstocreateacustomhook.Bycovetiothesefuctioswillbeprefixedbyuse.
Thefollowigdefiesacustomhookthatcreatesavariableadlogsitsvalueothecosolewheeverthevaluechages:
ValueNotifier<T> useLoggedState<T>(BuildCotext cotext, [T iitialData]) { fial result = useState<T>(iitialData); useValueChaged(result.value, (_, __) { prit(result.value); }); retur result;}Aclass
Wheahookbecomestoocomplex,itispossibletocovertititoaclassthatextedsHook,whichcathebeusedusigHook.use.Asaclass,thehookwilllookverysimilartoaStateadhaveaccesstolife-cyclesadmethodssuchasiitHook,disposeadsetState.Itisusuallyagoodpracticetohidetheclassuderafuctioassuch:
Result useMyHook(BuildCotext cotext) { retur Hook.use(_MyHook());}ThefollowigdefiesahookthatpritsthetimeaStatehasbeealive.
class _TimeAlive<T> exteds Hook<void> { cost _TimeAlive(); @override _TimeAliveState<T> createState() => _TimeAliveState<T>();}class _TimeAliveState<T> exteds HookState<void, _TimeAlive<T>> { DateTime start; @override void iitHook() { super.iitHook(); start = DateTime.ow(); } @override void build(BuildCotext cotext) { // this hook does't create aythig or uses other hooks } @override void dispose() { prit(DateTime.ow().differece(start)); super.dispose(); }}ExistighooksFlutter_hookscomeswithalistofreusablehooksalreadyprovided.Theyarestaticmethodsfreetousethaticludes:
useEffect
Usefultotriggersideeffectsiawidgetaddisposeobjects.Ittakesacallbackadcallsitimmediately.Thatcallbackmayoptioallyreturafuctio,whichwillbecalledwhethewidgetisdisposed.
Bydefault,thecallbackiscalledoeverybuild,butitispossibletooverridethatbehaviorbypassigalistofobjectsasthesecodparameter.Thecallbackwillthebecalledolywhesomethigisidethelisthaschaged.
ThefollowigcalltouseEffectsubscribestoaStreamadcacelthesubscriptiowhethewidgetisdisposed:
Stream stream;useEffect(() { fial subscribtio = stream.liste(prit); // This will cacel the subscriptio whe the widget is disposed // or if the callback is called agai. retur subscriptio.cacel; }, // whe the stream chage, useEffect will call the callback agai. [stream],);useState
Defies+watchavariableadwheeverthevaluechage,callssetState.
ThefollowigcodeusesuseStatetomakeacouterapplicatio:
class Couter exteds HookWidget { @override Widget build(BuildCotext cotext) { fial couter = useState(0); retur GestureDetector( // automatically triggers a rebuild of Couter widget oTap: () => couter.value++, child: Text(couter.value.toStrig()), ); }}useReducer
AalterativetouseStateformorecomplexstates.
useReducermaagesareadolystatethatcabeupdatedbydispatchigactioswhichareiterpretedbyaReducer.
Thefollowigmakesacouterappwithbotha"+1"ad"-1"butto:
class Couter exteds HookWidget { @override Widget build(BuildCotext cotext) { fial couter = useReducer(_couterReducer, iitialState: 0); retur Colum( childre: <Widget>[ Text(couter.state.toStrig()), IcoButto( ico: cost Ico(Icos.add), oPressed: () => couter.dispatch('icremet'), ), IcoButto( ico: cost Ico(Icos.remove), oPressed: () => couter.dispatch('decremet'), ), ], ); } it _couterReducer(it state, Strig actio) { switch (actio) { case 'icremet': retur state + 1; case 'decremet': retur state - 1; default: retur state; } }}useMemoized
Takesacallback,callsitsychroouslyadretursitsresult.Theresultisthestoredtothatsubsequetcallswillreturthesameresultwithoutcalligthecallback.
Bydefault,thecallbackiscalledolyothefirstbuild.Butitisoptioallypossibletospecifyalistofobjectsasthesecodparameter.Thecallbackwillthebecalledagaiwheeversomethigisidethelisthaschaged.
ThefollowigsamplemakeahttpcalladreturthecreatedFuture.AdifuserIdchages,aewcallwillbemade:
Strig userId;fial Future<http.Respose> respose = useMemoized(() { retur http.get('someUrl/$userId');}, [userId]);useValueChaged
Takesavalueadacallback,adcallthecallbackwheeverthevaluechaged.Thecallbackcaoptioallyreturaobject,whichwillbestoredadreturedastheresultofuseValueChaged.
Thefollowigexampleimplicitlystartsatweeaimatiowheevercolorchages:
AimatioCotroller cotroller;Color color;fial colorTwee = useValueChaged( color, (Color oldColor, Aimatio<Color> oldAimatio) { retur ColorTwee( begi: oldAimatio?.value ?? oldColor, ed: color, ).aimate(cotroller..forward(from: 0)); }, ) ?? AlwaysStoppedAimatio(color);useAimatioCotroller,useStreamCotroller,useSigleTickerProvider
Asetofhooksthathadlesthewholelife-cycleofaobject.Thesehookswilltakecareofbothcreatig,disposigadupdatigtheobject.
TheyaretheequivaletofbothiitState,disposeaddidUpdateWidgetforthatspecificobject.
Duratio duratio;AimatioCotroller cotroller = useAimatioCotroller( // duratio is automatically updates whe the widget is rebuilt with a differet `duratio` duratio: duratio,);useStream,useFuture,useAimatio,useValueListeable,useListeable
AsetofhooksthatsubscribestoaobjectadcallssetStateaccordigly.
Stream<it> stream;// automatically rebuild the widget whe a ew value is pushed to the streamAsycSapshot<it> sapshot = useStream(stream);
评论