flutter_hooks React hooks 的 Flutter 版本开源项目

我要开发同款
匿名用户2019年01月03日
135阅读

技术信息

开源地址
https://github.com/rrousselGit/flutter_hooks
授权协议
MIT

作品详情

基于Reacthooks实现的Flutterhooks。Flutterhooks用于管理FlutterWidgeet。有利于增加小部件之间的代码共享,可以代替StatefulWidget。

FlutterHooks

AflutterimplemetatioofReacthooks:https://medium.com/@da_abramov/makig-sese-of-react-hooks-fdbde8803889

HooksareaewkidofobjectthatmaagesaWidgetlife-cycles.Theyexistforoereaso:icreasethecodesharigbetweewidgetsadasacompletereplacemetforStatefulWidget.

Motivatio

StatefulWidgetsufferfromabigproblem: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.

Priciple

SimilarilytoState,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

Rules

Duetohooksbeigobtaiedfromtheiridex,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-reload

Sicehooksareobtaiedfromtheiridex,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.

Howtouse

Therearetwowaystocreateahook:

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();  }}Existighooks

Flutter_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);

功能介绍

基于 React hooks 实现的 Flutter hooks。Flutter hooks 用于管理 Flutter Widgeet。有利于增加小部件之间的代码共享,可以代替 Stateful...

声明:本文仅代表作者观点,不代表本站立场。如果侵犯到您的合法权益,请联系我们删除侵权资源!如果遇到资源链接失效,请您通过评论或工单的方式通知管理员。未经允许,不得转载,本站所有资源文章禁止商业使用运营!
下载安装【程序员客栈】APP
实时对接需求、及时收发消息、丰富的开放项目需求、随时随地查看项目状态

评论