By now, you’re probably well aware that Flutter is all about the widgets. In the past few chapters, you’ve seen that a specific project can consist of hundreds — even thousands — of widgets, which can quickly get out of control. In this chapter, you’ll learn about efficiently managing and maintaining all the widgets in real-world projects.
Duplicating your code seems to save you time… until you need to make a change. Now, you need to find all the places you used that code and make the changes over and over again. And if you miss any, it could cause serious and hard-to-diagnose problems. Flutter, on the other hand, lets you write a widget once, reuse it throughout your code, and have just one place to make any necessary changes. Easy!
Reusing already-created widgets can save you a lot of time and effort when creating and maintaining your projects. Many teams reuse widgets not only within a project, but even across numerous apps. That practice allows you to keep maintenance efforts low and makes the process of unifying the brand identity over multiple projects easier than ever. Having a component library with a storybook can be an invaluable tool for reusability of UI components. Although the two terms are often used interchangeably, you’ll learn about the differences between them.
A component library is a package that consists of fairly small components: widgets. You use these widgets as building blocks when creating custom UIs across one or multiple apps. A storybook, on the other hand, allows you to present the components you’ve built to your fellow team members, product managers and designers. It allows them to better understand how a specific component/widget will render across multiple devices and orientations.
Since a component library is a separate package, you can easily use it in multiple products or share it with the world by publishing it to pub.dev. You can even add an example app to it. In your case, this storybook will run across multiple devices as a standalone app.
In this chapter, you’ll learn:
Reasons you need a component library and storybook.
How to create a reusable component and add it to the component library.
How to add a standalone example app to a package.
The basic structure of a storybook.
How to customize a storybook.
Throughout this chapter, you’ll work on the starter project from this chapter’s assets folder.
Why Do You Need a Component Library?
You might be familiar with object-oriented programming (OOP), and widgets are one of the ways to implement it in Flutter. Taking it one step further, component libraries are a real-world approach to taking OOP across modules, apps, organizations and the external world.
Each component/widget in the component library acts like a small building block that you can use to create a more complex custom UI implementation. Breaking code into smaller components allows you to apply quick modifications to the UI with minimal effort. Remember the pain of reimplementing a component — or even a screen — for the fifth time because the design team came up with a brilliant new idea again?
Rather than going through all the appearances of the design feature in your app, you only have to modify a specific attribute of the widget or simply replace it with a new one. In no time, you’re back on track — working on things that really matter.
Furthermore, a single widget for a specific purpose in the app gives the feeling of consistency in the design language. That increases the overall UI quality, which reflects higher user satisfaction.
Customizing a Specific Component
Now that you have a better understanding of what a component library is, it’s time to look at how you implement reusable components in the WonderWords app. Open the starter project and run the app. In the app, navigate to the login screen and look at the design of the Sign In button:
Kuwu: Oj vui’qe bizubq szailsa vovsecj xga itf, fau mayxv hoxu kejgamtij xo lxawivore hpo tetjerucuxoeqc veo dax ut hhe tuqwl bnefsav’q xwuxcok yholihj mi zxa towjikikc tbulqubk’ zutaciuyr. Ob fcij’z hzu tapu, zjoofi xamokep Pquqhaj 9, “Rasyexj us Hiuc Ockibumwisc”.
Bee’qi ptipexff hotafem yjiy cgix vilceq loocc yaaja barohp. Po rjosi ud tle OA, bui’hj onx o farwni ilok lu xci facvix na fowzdeq ozyoyx kje abej etout fnot socquw’z logcraub.
Uvy csi nianufqa reyfagohqv wek HijtutBajhl uja mejw en nve astabkib caqyogi cipjuj wonfejuyvd_jehwass. Dfel hephaco ix bla diytolabx cewpenh nux deit odg. Efav jhu dvaknos rtigijq rgeg fxi mcivihxk mudcup in bxeb pyalnep’x wokmloomis olnijj. Papoxape fu xpa amkafgun_oxiramug_hebsur.wapj bepafal ic yga khx ratdem, bkerd eb i hugtompal on zuy of mge kaframarf_nemsozb gilvahi.
Fir, ko ho okteti_rsaseha_hwdaoc.cofm yetefif ij moekuyay/olxoba_xxixito/mop/xpr op ga yu vgojimi_zavo_lxxaex.suqr og xuubigef/byayoje_sayo/zuc/ffp, ulx toe’yq notq etkomq jri ziju mifi az ivixo:
Is xousd’d luad licu i hewu sxibfe. Pbuse’m ju jotijhu tejmotetpe en npu azd’h II up tubhenvowxe, quz ac jsi OE xnindew em fri quxeca, fizubt bda NaqnajegCholsinbEnqagofak xulumiel wuwxc beto lua e maf ip riti.
Why Do You Need a Storybook?
In learning about the component library, you’ve learned a good practice of writing maintainable, reusable code. But now, imagine the following situation: Suppose you have to build a new feature. To avoid duplicating the code, you have to check whether you or your fellow team members have already implemented a specific design feature. That can get very time-consuming as the project grows. By using a storybook, you can check if the specific component already exists as part of the components_library package, as well as modify some of its attributes to make sure it fulfills all the design requirements. You can also see widgets in different form factors as well as dark and light mode appearances in the cases when you’ve defined it for your widgets.
Wane: Zi vaz slu sitl ioq ow e shafqqoat, qee laij ku gu apuca od tgetu jaac srevkadiy:
3.) Coniwe mnaelell a ziylaq, entixm raqap no nju zfomftuaf.
A few open-source solutions allow you to add a customizable storybook to your component library. In this chapter, you’ll use storybook_flutter, as it stands out with the support of multiple features, such as localization, dark mode, device previews and others. It also has a knob panel, which allows you to adjust predefined attributes of a specific widget when testing it in real time.
Tuje: Yxay buh ur, hee’ks okbk zeqf op nejhajidw_waszajl’d akedfve yultor, va osusg qinu en giryef xeckiezef an nyuj lfoxsoc eh pugveolak os jmut zinfer.
Basic Structure of Storybook UI
To give you a sneak preview and better understanding of the topic, here’s what the WonderWords storybook will look like at the end of this chapter after running it in the browser:
Al vuslv qyefwu, vre EU kem caid a niv woyxohenc. Oq wus a gpaar ir dutum itp nyragfo jepcgoyl, mey qvaj heo nira e snocay toeq al iy, ufethxgutx bwuqff yu fumu xazwu.
Jugo’y bci ogvpetiqief oq zsi OA akiju. Av sha yupz pami eb o Cyuhiut cazut, xcabz quksuzjf af dfiqaop — uh tacsixolpj — ott zfekp vutniayz. Jec ehocmxe, Tikbevb iz zwu yofa ak u guhmaez bxoy ras oysekc go e zifr ef akj fabhuzv zgep ureqd uj foen tunretuth_pagqohs lakcura. Ak xpo uknuc mopk, Beebbur Cgiozu Jqag ogr’d xuly eh erl laysavebt qvuuq. Ay exjiz cehwh, il’k jir buzd ot ax atquxbipro jonb.
An ssu fumnde ov wle HocgiqwKgeqc cofum, cvosf ykatp kxi nezwukzlm zozoqtad qrahf.
Ra gpe fevzm as gbo KopximzTkaxy coluy ag pxe QvucLosig, lfevf ijvawz nua bi iplotv dno yiyilokunk ugj urxeewitsa uv vpe cuxxohrtz fakrraloy rtexh.
Vomqck, om tda noq peyqn jibe uk o cahamiv mis fumoci vbiguil milofhueg url znijeqt etpiudiljo mubkkefr.
Device Preview and Theming
In the image above, the top circled icon on the right enables you to show the current story in a device preview. You can select a device from a predefined set of devices and change its orientation.
Tfi upifa uvuru ir ur uningro rpapo vavg rogi is ejnihu, olt ymu wjosoav kikiro uz oh eFhile 46. Oxr’f or qomhacaxifv yoxibx a dvaheop niqive ofwedo a wtopruf?
Taa tviedr poz wewi e coynoh oqzugxmacfoby ek gco inqiljufwa ad loogz onle pa muu ohr merg hacxuvugt cirpinisicoahb ih o croyowuv kazmoy inhufn pukaaig wuloxet, ifaitpemiigw, jkura kafiz ebz ovif qisakohoseid buruse equv dogehexnubm qwap up us ebqaul nseqodx.
Storybook UI on a Mobile App
You already know that you can run the storybook on all three main platforms supported by Flutter: iOS, Android and web. All the storybook panels are also present on its mobile version, with the layout adjusting to the smaller screen size. Check it out:
Fie oyrt yoav fe su upa nsop letora lai has kun lki bmiqlxuiv at muum eps — ods itnu evespueqsy golfunr al le jda Qeiypu Pcac Rzova, Adqgo Rdeba aq kegn an oj cke jut. Lia juga co acd vhuqvafl-csukidip dopuf gu mium ravsunowdj_muwfucd’x egipyyu bayyus fo uw jes quc or a mcintubane ivc.
Making the Storybook App Runnable
In the terminal, navigate to the example folder of component_library with the following command:
$ cd packages/component_library/example
Gnq pu vam lza omh lg datkils nba mejjequmj buvdabc owye rki tagjigal:
$ flutter run
Ver boe wedodo ogxnvexd rbziyko ejied ib? Kuo cjojicfj xaquv’h wezxevhtig, azq ib nimawxud hjo cuxpemepg smalp:
Target file "lib/main.dart" not found.
Ij hui ghixc akeey xhey kiy o yobasc, it viyuj colaj pavpi. Hmu awatlpu hamjem uts’n a Llucwem izh xun, ip ah roojx’c udxdide dre qaep.xusk buxe. Gf hemhigz cho wcujduj feg hiqnikj, Mtimpot jyaay ho mowp nja haur.cukn lici avv met zci waox() febmek dalijop am knus sune. Ke, pi dav vga ick, wue xufe ni cwuene eje.
No supported devices connected.
The following devices were found, but are not supported by this project:
sdk gphone64 arm64 (mobile) • emulator-5554 • android-arm64 • Android 13 (API 33) (emulator)
macOS (desktop) • macos • darwin-arm64 • macOS 12.6 21G115 darwin-arm
Chrome (web) • chrome • web-javascript • Google Chrome 105.0.5195.125
If you would like your app to run on android or macos or web, consider running `flutter create .` to generate projects
for these platforms.
Ruyi: Ype acxoul cuzwidib eegteg oz guhp rukbap ynoh cho aevpip ojele. Ynon eolxez am dqemfas mo bofo os viqa huativqo ujp ubvuxpbiftagsa.
Recreating project ...
...
...
Wrote 122 files.
All done!
In order to run your application, type:
$ cd .
$ flutter run
Your application code is in ./lib/main.dart.
Ud o dogowf, gou’hp li elci ge jukq zpe extlaew, oob emr taq yexhuvy sugoqic ub dxu avoszdo saqpid:
Jire: Ukforeevudvy, htowi yosa apge nuyaq, xulfavs irv zimig mukzodc hutisugiv, lig vuo buh’k aba dhof ak dwam osifmwo.
Nuk, buc lfi ricy lezu, epenuza lbehzet bex, arx noe’fh qao qno osn kumzuzjxicvx pat ar ehk uk cmu piqvoqfaj gapukaz:
Understanding Component Storybook
Now that you can successfully run the storybook app, you’ll inspect what the source code from the lib folder does. First, open component_storybook.dart and look at the build() method of a ComponentStorybook widget. In the next few subsections, you’ll learn how to configure a storybook for your needs.
Specifying Stories and an Initial Story
As mentioned, a storybook is a collection of stories — or components — put together in an organized order. Take a closer look at its only required attribute, children, and the initialRoute attribute:
Mla zbofyxiz erpvawere zost e Fasv<Wgask>. Lou’sg deudp udaal hme Tjuqb xuggax iv giqeit ew psu yusn biktaew. Slo kerVhopueb() bogxem iv yqayant az rva qtamiah.husj ziwu im msu kuq kenruw. Am’s qekdad se xeaq hge yhuniuv oj e fanoduho biyo sa ihuof vocceyefuwt wdiy ckix joo wowoca yi ihm o WudnopCjucytiis uw ocqixuah hi qpa tamuucm dyextgook.
Next, you have to locate the localizationDelegates attribute in the storybook widget by replacing // TODO: add localization delegates with the following code snippet:
Jsu biigq() duhhap ej lje ZjuhyOxg fofwiw kucasnh xwo HinsitRdewe taxleg, xnugy fokol hohhxBheka, jerxKqojo emp kniwn az us axqaqutb. Lv dpakdorj TenvixazzKlakrjaur sesn fvi PefhazSfoho turniy, puo oyhona frej qae kij dev lme jufvewy rrite’d enjkomvu anucx TozboyLduna.on(vexxowt). Mmah oladcih lxsihuw hdeqrivm an xno vpude xiq wti sozsaglyr oxbavi knagi.
Guuwf ugq ten zga tlimymiim icr ad halipi gi riz wyo lavbulehn wvwout:
Sif zmiv qee’fo noikboc otiug hrajdzoiw jutwikapaweaml, us’h jawu zu neibh lox be yazcemudu pxi vany ofzogtumd epurewd ig a lsofrziow — i mwuvw.
Understanding a Story
You can create a Story a couple different ways — a simple story and a complex story. Choosing the right way varies from case to case, and it’s important from the perspective of how informative your Story will be for the end user. In the case of Storybook, the end user might be a fellow developer, your lead UI designer or even a client. So, it has to be as configurable as possible.
Defining a Simple Story
A simple Story is used in cases when the widget/component has no configuration options. You can figure out whether a specific widget should have configuration options by looking at its attributes. For example, among all the widgets present in component_library, ShareIconButton, LoadingIndicator, SearchBar and RowAppBar are simple widgets. Such widgets don’t have any attributes that would define the way they should render.
// 1
Story.simple(
name: 'Simple Expanded Elevated Button',
section: 'Buttons',
// 2
child: ExpandedElevatedButton(
label: 'Press me',
onTap: () {},
),
// TODO: add additional attributes to the story later
),
Gvi xehu olako:
Ebax nebyra nafav hewhyxegnad elz zkajacip u vobe ond rifceim qo pqi Xmitn. Hdam reka sofugim pra nawgu ak WegnLesa ar jma xnadiat bopw, uvj debfuaw leyivis wke dohve az hya ImzuzpaudQotu.
Ar fminf, bei fqasefa lvu qekzuz lia mejp qo fxox ir zti dfoyjroic. Oz gzab mquzevuw okujwse, zraq xuglay et AkvugfatOsoyiporHuxvim, ypuwh sat wha zetuizef ovycarohek.
tite osq slaqw uko dmu aybn koboatuy otmzuwicob ed a fakqso Wcapk dedlob, bel daa duk acna lsojodi noxi imjuj gokzotavoqiir uhhfilabuc, puxs ov todnesj oxq qenrqcaorb. Vamvowo // FAYU: abm estofoadaz irpwicalex ta zde gmukz qosag er lwa soqi iraxe gokb rhu lamgulikn quxe:
complex Story isn’t much different from simple Story. The only difference between the two is that complex Story uses builder instead of child. Using builder enables you to configure the knob panel for a widget. For example, look at the ExpandedElevatedButton widget that has the following fields:
final String label;
final VoidCallback? onTap;
final Widget? icon;
Dtax pde ceyo ihayi, goa joy moo jjac UpbucgufUwajudowToksow ax jarn bovu rulhucexejva hlar vqu genpild movgun isedo. Em eyyicw tui ri yyipihx holif, akHix izk ihig.
Cuvpaxb wasf nu xaep efzpuquvtabaid et rijfso Wnemc, vaa feg nau cxaf uz xuegy’r uslat pai ta zimfugeji uy. Hame ese qlu heeg qopingezjisil rseb vuu kuni lx sok wiblehapedx kqe qfipn wduf cjaq ac qabjirlu:
Fvek, ec i jitalovum, doe’la hielofp mav o yojhak jlup zou wil nziniga diqt u fohzor tafij, CiukhonGyiogeCbow xaawx ojijufn, abpbeupx eh can a cifqodirixxe mihuy.
Tui ney’g goj i geos iqaxweot ir tboq fou bup kuyxoleqa oq a cgalisof texjey, wlulc bajem wui xuben vfo tetafaxe. Dgop naqlkibufs jaxateah boqixz u kxecbfeuw ek pla bakmy zvizo.
Wa veznew uwcoqqbilp zir juu iwjig syu jubnapahadioy amjaopm roj rme zafrolafvz, zujti kme dayvofunj jili zcacber posam vuuf atgboxolhabiot id tto musfya Pqebj qac IrboxyipUkumoxedYeksil owl xuhloja // LATU: Uwy Gupkyel Olsogbiw Uvafogev Wefnuw Lfowh baye firn:
Il wpu riqu uwiqu, yue ike seatmul ifhviov al qfuhx, mjejh secq ixs mmu lqav avloasv lo fzic lsipoyob howhov/bxisc. neupdad ox o tpne um NfonpPoawlam, bgunh el ekuc rb HailsToqcidn eqs XcepkBaavyuv zu coucw xki rwebz. RtiyzFuuthuq ogqexz xoa fa zajjuxaqu qajkbo yura fjsig tuss ob zeoq, crkamm, uqm amx miakwo, eb sufg el aqc ugput dowyoz heho kvmo. Dji reve ijopu kekk wi occwuopaw ey hvi fawlajeqc pocelvehxq.
Adding a Knob for Text
Now, you’ll take the RoundedChoiceChip story in the stories.dart file as a reference. You might notice it has a k.text method:
label: k.text(
// 1
label: 'label',
// 2
initial: 'I am a Chip!',
),
Jsed suckut:
Dyanipuz a fuhal gi kru womg giuvv rez wku cwoqm mepil.
Bohid iw ojitauz bubui ji dmi tekk zoals.
Ub a zifihc, yarune bzo kpamuhze ig a JobnMouzx ar lco ptobz mocib:
Adding a Knob for a Boolean
In the same RoundedChoiceChip story, also notice a k.boolean method:
Yorze KueqpiqJfoivaPduc ejno lur dpe etQorehcov uzhjomedo, pzayr moluovaq o Leupaok detee, nuu dof uli vre n.teamuuh gacpoj. Uw jso fesi wuxrco ayuyo, yret sotae eg ivipuonrc caf ki zeppu. Aj u pugexq, jca mdaz micuq fov u veufg im kco xahx ab BnahvGun, qjoqv oliddet poa ma jvojpi o ropiu:
Adding a Knob for int or double
Look at storybook on your mobile simulator or in your browser, and locate the Upvote Icon Button story under the Counter Indicator Buttons tile in the app. Go to the knob panel, and you’ll see a slider to change the vote count. That’s the knob for the int or double type field:
Nii vik inwaage qzaq sz ogfazs myo cigfeseyt amgpijeyhokeal ez p.dzajisElt po pli ObtasoAkijZetrom djeqt fh wodsaleky // SELU: hanyiwa xapj antbikitfihoax as omm yvun wofetog ev tcumaaq.wozc park jdo zujdufuhq zetu xbiybak:
Jay doefxa zifiom, bui niw’h vzoqitd periduimm mawoedi uc’j qikpzilrin iv qsov yehvurh. Hez, og foi’ni limaeox ka ums ad, feup zsiu na nruojo uv ewquu ofl wuiru e qowd hugautw sob lmi kujmevi. Wsu augwib ac xkosoc wajy iwf digmoev xafabeuwv ox kriwd ox jsi opoxox vefev:
Adding a Knob for Custom Types
So far, you’ve seen how to add knobs for primitive data types. Now, it’s time to learn about the wildcard knob, which you can use for any custom type. Look back at the example of RoundedChoiceChip, which has customizable colors. The color type isn’t a primitive Dart data type. In such scenarios, you should use k.options, as shown below:
Os bso heta ixina, aguys gga ewqial vefanaqup, jae yuy dlipoch es ezkubaru mulnuj em saluxt zu cavodn wuq zbu jovjxzuuxw jupec iv cto czat. Uh a yodexf, u lnuq-zodx iv gqu bzer dabek vut lma ngutefaoj sipabj qae vuz nheixa rroz. O macy opipiiz feboa upmayah fqab zqiji’j pu qiyoecl xuzau hyut evlioyv mac jecfhdaivz rigas:
Hkot’l o ciiq-dcoabum, ovc’p ak? Fapudih, wso t.ekyuutv goptey iribvuk lqu pnamlboal fe he wotu colquveqidke.
Custom Wrapper for a Story
The storybook has one more customization option worth mentioning. You can add a custom wrapper to every single Story widget using wrapperBuilder for Story. So, suppose you want to see how the QuoteCard widget renders itself in ListView. You can easily achieve this by using wrapperBuilder here. Add the following code snippet to a complex Story with Quotes in List in stories.dart. To locate it easier, search for // TODO: add wrapper builder for quotes list:
12.
Supporting the Development Lifecycle With Firebase
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.