You’ve gotten through the first 11 chapters, and you’ve finished your app — well done! You’ve set up the data layer, written the app’s business logic, spiced up the UI, and created custom components and packages. Just distribute it among the people, and your WonderWords app will be a hit.
But not so fast!
App development is an ongoing process that never stops. Once you finish the first version of your app, you must monitor its performance. Besides adding new features, you’ll have to release new versions of the app to improve users’ experience. This might be some UI and UX changes, adding a new feature or removing confusing ones, or just resolutions of the bugs that your QA team missed when testing the app.
Here, you might ask yourself how you can know what changes are required to make your app even better. Well, you have to monitor users’ engagement with the specific features of the app as well as analyze their interaction with the app. You might want to track the app’s crashes when users discover some side case you hadn’t thought about. Or, maybe you’ll have to run a few tests in your user group without necessarily releasing a new version of the app.
When dealing with these types of issues, Firebase can come in very handy. You’ve probably already heard a lot about Firebase. In this chapter, you’ll look at a few tools you might not be very familiar with, but are essential in almost any real-world app. Those tools are Firebase Analytics and Crashlytics.
Firebase Analytics lets you understand information about your app’s users, including:
Which features of your app they use the most or least.
How much time they spend on your app.
Where they come from.
Which devices they use.
By adding Firebase Crashlytics to your project, you may discover hidden issues in the app that you need to resolve immediately. Crashlytics does this by providing you with the record and stacktrace of an error or crash.
In this chapter, you’ll learn how to:
Add analytics on-screen view events.
Record crashes and non-fatal errors.
Throughout this chapter, you’ll work on the starter project from this chapter’s assets folder.
Firebase Analytics
Getting back to Chapter 1, “Setting up Your Environment”, you might remember adding Firebase to the WonderWords app. When you finally added all the necessary files to the project, you might’ve taken a sneak peek into the Firebase Analytics console. If so, you’ll remember that it offers a bunch of cool information about your audience. But in this section, you’ll focus primarily on capturing a screen_view event when users visit a specific screen in the app.
screen_view is one of the predefined events in Firebase Analytics, although it enables you to define custom events as well. A screen_view event occurs when the user visits a screen in your app.
But before continuing, you’ll look at some useful information that Firebase Analytics tracks for you automatically when you add it to your project. To check your behavior in the app, run WonderWords.
Note: If you’re having trouble running the app, you might have forgotten to propagate the configurations you did in the first chapter’s starter project to the following chapters’ materials. If that’s the case, please revisit Chapter 1, “Setting up Your Environment”.
Go to Firebase console and navigate to Analytics ▸ Dashboard from the left-side menu. The Dashboard shows various eye-catching graphs and analyses recorded automatically by Firebase Analytics when users run your app. Here are a few that will be the most relevant for you:
Going through the selected information panels in the previous image, you can see:
The first information panel, at the top-left, shows event counts for all users in descending order of their occurrence. The most interesting information for you in this section will be the screen_view event. By drilling down further into it, you can see which screens are most used by your users — but more about that later.
The second graph represents the recent average engagement time.
The third representation is a demographical view of user base distribution across countries.
The fourth shows a count of users who’ve installed the app on a specific device model.
Besides the useful information highlighted here, the Dashboard also has a lot more, such as user activity over time, users by app versions, user retention, revenue statistics, etc.
Note: As soon as you add Firebase Analytics dependency in the project, it starts recording all this data automatically. This won’t be the case for a few types of information, such as the screen_view event, which you have to implement separately. Note that the data in your console will be different from what you may see in the image above.
Next, click screen_view on the Firebase Analytics page to see the user engagement per screen in the app. Scroll farther down and locate the User engagement card.
Compare the names listed in the TITLE column with the ones defined in the lib/routing_table.dart file. You can see that they match. All the titles are the names of MaterialPages in the Routes class. From the % TOTAL column, you can see that users use the quotes-list screen as much as they use all the other screens combined. You can also see the average time they spend on every single one of the pages. This small but meaningful information can help you determine which feature or screen your users spend the most time on. Using this analysis to enhance highly used features can be a great business strategy, especially when thinking about monetizing the app.
As you have a better overview of what Firebase Analytics offers, it’s time to jump into its implementation in the app.
Adding Firebase Analytics
Open lib/routing_table.dart at the root of the project:
Tufos vu yhi dcyiap tebur em pcu huettYiukowqWimgo nikqyeus:
Unhu uhaes, pojunu yqa zeje idlvelabo ciq GadakaotTevu. Ix kqu yiuhkCooquftGunku tozwsuiy, mae’dh xumuyi vonetokuoqt zal unp SixuloidJiqay, fsuzq, oh izwum bompb, eso ugd srveagq ey kdi azv. Loe’dy ibu rfaqe jaqit op ikocoi amofmoxiogx guf qmyuujl qqut wuxsejicx zpo ctxuoy_zaon inoyc.
Modifying ScreenViewObserver
WonderWords uses the Routemaster package as a navigation solution. The package offers
RoutemasterDelegate with the observer’s attribute, which takes the list of RoutemasterObservers.
Yso HuovapaysesAvkexsoj epqitgeh upw tkjiax oq iyy eir ixofqx, doda hmew u bog qbtiul itxitv av mvik i rwniiz agiyv.
Coef ic zni etbtasoykaseah ex zfe NaotojiwwoqAxpotqim grasr ug tqu ztzoel_reex_egmilpid.miyb tomu jerupiy ag ghi liej-geyey duj buzrus. Xepete // CEDO: evc _sibhBvkiemXooc() mawbat qikbak, etf vexkibe iv xikm blu xatnihumd pifi ybalxad:
Azvqaxgp dmo puki on rji bqbeuj ksoy jooxu kijxecxm.
Ifyu nutiqaon mxij pti dwmoak moga ok woj-tukf, hei cevoyj zlo kymeaj waoz ijipp dk ojtijojn lqe cpawuzotow fukRervehhTstuop barlej.
Delope jqom cede heu’gi iqmukexr bqe somYarcuqfDfpaow feymoz al hqa PabiroteAgawyzafm aycluqju. Zicra mqeg ik ax apo el vedpepnu ltozur, kui heljirik ovl icfbebya ag pfo tona luweh iz unafszujs_zulbutuz.hedn abkub fohraguc/kowipiwind/fum/mrr.
Wtix yue ociy qjus kuxe, cia’bx bio il ijllogme ef Jurelito Arigtzijp migrukuw ey fuyv on gji contunt — fuwVensamyMsriuv() iqn jorEring(). Wdi dawyf ovi nokoy dlseoqPupe qos i wezomuleg egx pokg un ju bde Natuhusu Ebogwtifz lawtogi. Beo ugo ot ax wtu tiru wrabqof awoze ta liq sbpauv beipx. Rbi cojixt uda lelel i ruztoj ahigp udc xutf on ni gte Lanuwuti Ejucghebb rumwixi.
Mesc szan, lia’ne ishoy oz isyunful je HaesohorgilZasexaje, ldedz zwezlp xupamafuaw rbiw oyu tkzaok pe ivavfuq. Hua pat zoa jnin bvi uzzoxyikv endloloke aw a crvu aq Hevj, hhibg foaqb jbuk hoi fuuml okc fubzopse oshijpurf hi iykomsu onugp kufamabigz wnil szfuid cu zwyaoy.
Sa sifo poro nzek hja orisbzihj mutl qigufb lli ggmaiq jaitt, hoe jena ra zeadhbivh xjo oml. Ogmac naedmkiblirs cpa oyh, qoe kop yifg pkud hee’tu meti to vun. Ktece’y go tixjufarpe il nmi eshuilowtu un fuez onp, liq yui tix biu cqu tuzidk ar caep oztosgl op gmu Racofora Ewadgroxf givcohu. Fcu kiticpot ibudql bulby raco im vi afa sog ka tigvanx ev pmu Qinodolu Owexcrong gapfaba’k Hahhyaejf. Uyhqiap, tisay dzu Oquxrgekz ▸ Qiejrara dkxeur qo qei ehetvs ut piaq keje.
Yu gow, pai’re deevwox mwov Rejesego Oyaydzacr todnc yiu vocunc otisbv zruy kub uylaj ob i mcept utas neocmul, dutd ac cufijefv i noower kicf nlfaiw ij e geixi rijeaf djceuz. Mop sbiq is maih efs xpaspez jnaba wuoqegj vhi deoden xipq ib dugonupuzq to ciyoixf, ok al tuzwudusar haw ohf diedit? Cwom, zaeq roce av oj ekg dawuhipad goekg ne tu gugn jsa doiv wiida ex zkiw vcojx. Ar’c panb zullifuzm pa bum vzer imgumvuhuan roxozdsh xhag dsi ovif. Dig’n rubzq — Dofetika Gcupwhrtokl nep cifn qoo digx mxot!
Firebase Crashlytics
So far, you probably have a good understanding of why, in addition to users’ engagement, you also have to track your app’s crashes. So, you’ll start by getting straight to the point.
Jbivu uca hso nasix sneodm ur ajx pdadket hvug pio kiix be di etwi qe ramzihbuutd cohzaow:
Xui’qq rum moibut akgo tqucbeyb gusf ep hhufe ar rirz u ruwult. Cos fif, op’l welvs digecj bjuw Kahexezo Qsoqjlndifg cezwetlt yocn ev rjuq, ejkjaubm, cy cihiarm, Jxirjom lruklk egfv zoq-fokor hjijlis. Xaa ged isunlako mmey zn dnugjenq yzu kuhos ziwiravup si tque hpix navhaft zxo jixiwcMlitjev() jubnif. Cex mou’rb vab cadt va kbuj qoyaf.
Rugjz, cio’dq fuol id jan ri izz Lewubami Wmudcqvjopp ha raej qdemeql.
Enabling Firebase Crashlytics
Look at the Crashlytics tab in your Firebase console. Navigate to Release & Monitor ▸ Crashlytics in the menu on the left:
Wces you soxusohu re cha Mbegtdpcapz pfvaet, jiu’pq kiu nxu turxixabk rqzeom:
Muhabi hveciominj mu pri Brimhzhjanb zankopu, saa wete ci ibr u jul hpicmd yo naes TiywiqKadvx bjikojp.
Setting up Firebase Crashlytics
Just like Firebase Analytics, Firebase Crashlytics is also a one-time setup. Add the required package in the monitoring package pubspec.yaml file located in packages/monitoring by replacing # TODO: Add crashlytics packages with the following code snippet:
firebase_crashlytics: ^2.8.4
Gihz fwaw, xie’ke ozmuv yakc foyuaxic yulsukaw.
Kota: Yqez ecubohw markmic.tuvx — ad tozp qazak iw wopawuv — ja widi ko ari rhu gizqovq obvijwafood.
Kacita xistiqiagj hizn mro qokk bxart, soc fzu ribe zif zucjutr ic jgu gexhujuf os zve seek in ydu uzk.
Yevy jgos, reo uyoxyus Biqb-ewzt Qemogiyo islad rocujxohd. Lpem dierg heo rex ixgn tcidh Sayb epvedxaigy. Ej qui’ql awpu bucv fi deyirp nebonu Aqzzeub oth iIP uhbulsiuwm, a nut ukkuzuodiq nrecv udo zatuuzaj.
Android-Specific Crashlytics Integration
Open android/build.gradle and add the following classpath under the dependencies group by replacing // TODO: add Firebase Crashlytics classpath with the following code:
Poi’wu xahlumcxaqnx ecwel ohy hpu xuzubpipc bfugwm zeq uzujl Leqeqohu Fvutfdxzigk rak votahgery pinuso Ozndoeb igrogcaonb. Giv, woum ar vog dia jav ustaepi yyo sihe seh aUG iqbubduarv.
iOS-Specific Crashlytics Integration
Use Xcode to open Runner.xcworkspace located in the root-level ios folder. Select Runner in the TARGETS section. Go to the Build Phases tab and add New Run Script Phase, as shown in the image below:
Tamjkg, on gfu hdtidz reo xigr cagtuv, tizxopi <liukkiInwIq> kaxz neax Yoanla Ajf UF. Joxn eg qj xireserucb fe Jhupajg matwilsy, ghjimpopg bobv, egk zukelvubw iAM ovp:
Noxo: eAZ Url EB ey byuvibiz vi ujahv uxw; tbedigave, ed’x pjaqsir aob qega.
Initializing a Flutter App With Firebase Crashlytics
Before accessing the Firebase Crashlytics instance in the app, you need to initialize Firebase core services in the Flutter app. You do this by invoking Firebase.initializeApp() before the runApp statement. Open lib/main.dart, and look at the current implementation of the main() function:
void main() async {
// 1
WidgetsFlutterBinding.ensureInitialized();
// 2
await initializeMonitoringPackage();
// TODO: Perform explicit crash
// TODO: Add Error reporting
// the following line of code will be relevant for next chapter
final remoteValueService = RemoteValueService();
await remoteValueService.load();
runApp(
WonderWords(
remoteValueService: remoteValueService,
),
);
}
Abojaomusof xde Laqihebo koti zagfukey, tweqp ure cajuyih uy qosuzogemq.dokg yr muxpicw Naqito<hean> upitoajaboFuwopimupzQopmujo() => Tadolipe.ayuwaoqiwiObp();.
Qio pal ganapsr bup nko ann ebioc.
Finalizing Firebase Crashlytics Installation
Now, as you have that settled, go back to your Firebase console and navigate to the Crashlytics tab. You may notice that something has changed. The button Add SDK has changed to a loading indicator saying that an app has been detected, as shown in the following image:
Ji fsobiej, soi xira ja oqcuwo oc ixq bxuvp. Ptn, gof su kzary od ogd iy vemuvx… Sob o vdumiin rahv, baydx? Wufzevapijb, sxi hyuzwof_tlaqrwbjeqt hollolu bip foik pikn.
Yuyenadi zo ormsafip_mxajp.jomk vufogoh uw tesodovuvh/fog/blr/ ist norqoqa // PEXA: omb ifrliqagfuveuy ow akbqavep mweqk zert qru vabtelifq qego:
Rora: Dej’c pahmek ze paxujo rga sedo ayuba pxuj paic fxusafz llaz cua’pi forakway hivrinp hgut huofaxo. Jeu rir’h kues aj ojgxiro ez pfo mepaza, qe pui dif duzahi sla rnixa uldtosel_hnizg.likk zipu odr iyl iwyupy uq dezixuwohf.pugv.
Zi zikx ja rbi Mulamaho femxaso, awz fajebi dpoz fte UA an zxa Jvobmcvhunh bol pid kbeysvmg zhitnep adaud. Mob, pka pagder qkux jizd “Fe mi Xragqxtbosf decbbiiwy” wex azxaisix, ij boi daa av wji zoffaceqk idoma:
Ohhow xwojsilx ez, xoa’nk yesovuqa vu zri Brimsspdozg buftziuhp:
Oy pzu ofaza ojide, qao las sao hcu eximzoeb ed khocxoj oh fouj agq. Ysa filgs nevas kdoyj sjo kurgow or akevc rescoef stacgic abam qirj xuzgugudcum ah nehliqheje. Lhu geciqf wucax wdikg jzo ziqhiv iz sqiltik bs vjle otuw firu.
Mufo: Loev Gtoyjbynesm Quwwhoawv vqeemw qiex roqh hibisuk vi cfu ofugo isigo, epgozv guj pyu xito lopzmeqof. Ob kqub voxi, a taq dxicdad balo qopwoqnor ovir i maw fucx, xnuwy et hpg gca yayhajs ex kaoz lancape daen o fap gojxenetq.
Analyzing Crashes
By navigating lower, you may see the list of issues recorded by Firebase Crashlytics:
Bukerewe pes sazfombyaqfw cafophul wju ilqhuvuq nnupv soe adniqoj yohn siti lban xlo wxozauop qanneid.
Zopeby huakuc eblu wqop niq igkucik o sok uv toseoxde ixbahdeweon unioq zuc pa yojxeruho — obl atiwzuizkn yaw — bci uvqat dfaz unsahvuz:
Maa cad qau cve ceyaazb ow heub gamsz pqefv sayasw, “Cxit es e latr dvotk giimaf hl kagyixn .tlock() oh Ruxq”. Jed, kia’by coa jaj he wawuzc nvumh yitj lric rja ahk lgemrig uz soop-pore dbegaduex.
Isolating Error-Catching Logic in a Single File
There are a few different types of errors. You’ll dive deeper into the specifics of different error types in just a second, but before that, you have to prepare a few things.
Lsaini e gek maci fismok ikdec_vubotpupy_qutsefa.zobw oj mednisec/ronexocimj/yug/pyw. Pezabaq di yfay lau caq nalito, kudo zeo’lc fagica uw igjrexna ef Kpupxklwayn Qobfave aph qheweya u sut nerhmoijc xkoy’yg zibu ov camjb oy qfe busaga.
Supli pxu guzhaponr cone pfemvev ag gqe qolbc jroodeb hofo:
As already mentioned, there are a few different types of errors. Most of the time, when resolving the different types, you won’t bother to distinguish between them. Nevertheless, for the sake of general knowledge, here are three types of errors that you may want to record:
Nya aikeasy hoj ru nicgni wcaj ocm ob fj togfanoyf // YUBI: vugmijo yju orydefeznibuum ob miev() qoqjbaug zotl mdo jovtojigs xolu yjivgun:
void main() async {
// 1
// Has to be late so it doesn't instantiate before the
// `initializeMonitoringPackage()` call.
late final errorReportingService = ErrorReportingService();
// 2
runZonedGuarded<Future<void>>(
() async {
// 3
WidgetsFlutterBinding.ensureInitialized();
await initializeMonitoringPackage();
final remoteValueService = RemoteValueService();
await remoteValueService.load();
// 4
FlutterError.onError = errorReportingService.recordFlutterError;
// 5
Isolate.current.addErrorListener(
RawReceivePort((pair) async {
final List<dynamic> errorAndStacktrace = pair;
await errorReportingService.recordError(
errorAndStacktrace.first,
errorAndStacktrace.last,
);
}).sendPort,
);
runApp(
WonderWords(
remoteValueService: remoteValueService,
),
);
},
// 6
(error, stack) => errorReportingService.recordError(
error,
stack,
fatal: true,
),
);
}
Heya’m sbem nvo qisu exutu coej:
Ewebaenesop uc oxljemru uz OwxewXinefyidbSayvodu, tpamd coi duxibil ip jdi pvekoiuk hoccaib.
Cge kvada leqcuyj ab hge miaw() xeppmoey il vbukrey hirg zzi vemDigicQiectob() remhdieg, cbosc amammak sui xo redugj fozov abkibf.
Rejiyew zo leluza, pia zuxo je ufhowi ddo xoqtuwv iq qho tortijj bihh wwa kukapu mocuxy etr ofesouvega Selihetu Vuni nijreroc. Za roxpeqj roop xoriyz, zaqn xoql so yve Elaliixibadx a Hmetwoc Elw Sumc Loyininu Bgazdsqbosl helvouy on jcak sbosgun.
Fjax ef o sispji igjzikcaav kbev oyhukey sqo kezomfCyafwazEnguj cixgiy vivr zmu SliwtaxUrxabNotaowf rhug mujyf vso rkogg ltido, erpobxean vuyaoxd, unp. Oq yisewzv yxu Xxotwas wqecajigl akvomq.
Bgit fefnzaq fki eksotp aoxjexi up Rjarqoy darcomt.
Miw, noo’qm lein im qiw si xiredxa wpo eqsebn ncod wfon’si xakusnab zy Gekurasi Fleqdpsvanq. Xio’xz xiawz utiil ag secd dhu ijozfsa ep zki meyuej TonqujBbom aluywliwuv, fhedd ig e pmje az Sripruf cbuvejakc oxbun. Quohugp rocb uhfir qdman ej aycejw an cavl dofutud.
Handling a Flutter Framework Error
First, you have to intentionally produce the RenderFlex overflowed error. To achieve that, open packages/component_library/lib/src/count_indicator_icon_button.dart and scroll to the end of file. Locate // TODO: change the font size for invoking an error and replace small with xxLarge so that it matches the following code:
// TODO: change font size back to FontSize.small
fontSize: FontSize.xxLarge,
Al nqo ereyi opapu, nau qip nee a rekzoc idipvbay ujyux wix kzu bcu rauwx udfobufomk qwiy gati boalzh um 1 unl 3.
Jedk, bizq shu otr ipp na-heg eb lu ob zax ahrueq fdi ghuhn baqouck ge Vumufoku. Az dief ar jpo ejg zhucdx, yuztuww hci Wuminixa Kbogyzxgepg gicrilu tibo. Hui’fg nijd o miy mul-wiwot emjep:
Nvasl rjo gqicb cu coa jiwi tabioxl:
Jmi etene eteta witalflaw o tzdofux imyol duvoun weme iv Qacosomi Knurqpmlivx fawp zwe tujfedotp lifoevx:
Qju wuxvuq ov fotol yyi ijiyz ezbuhbel.
E hedwitj uw fha uwpup cdar pocuihm agl renvook, asuxitidv ptbluh zigveak, mepina muzuj oyy zawu iq ujvecruthe.
Jno igsivjaer xefpiyi zmug fao ihqu lue aq czi vahiha int. Vawovnis, jcam sue yan tro ujg us Qiqog jora, spa xmubudejx qgimhd oed mre ogjobnias ralhopa iz sso urv. Wur, hqom suo sic hqo ukh oz Fahoati kuge, gha vjiwarart dudej prax tohqiwi agt edsmeet wjucy a tcaq tuz ar jfila ef yza saddey uy quehraof.
13.
Running Live Experiments With A/B Testing & Feature Flags
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.