<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Jakub Homlala on Medium]]></title>
        <description><![CDATA[Stories by Jakub Homlala on Medium]]></description>
        <link>https://medium.com/@jhomlala?source=rss-4952f6cd91ab------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*QoS3Iit_0kaAdPWgXCkuow.jpeg</url>
            <title>Stories by Jakub Homlala on Medium</title>
            <link>https://medium.com/@jhomlala?source=rss-4952f6cd91ab------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Wed, 15 Apr 2026 21:00:09 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@jhomlala/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Inspecting HTTP requests in Flutter]]></title>
            <link>https://medium.com/flutter-community/inspecting-http-requests-in-flutter-9deeddfe8d1?source=rss-4952f6cd91ab------2</link>
            <guid isPermaLink="false">https://medium.com/p/9deeddfe8d1</guid>
            <dc:creator><![CDATA[Jakub Homlala]]></dc:creator>
            <pubDate>Wed, 13 Mar 2019 07:57:56 GMT</pubDate>
            <atom:updated>2019-03-14T13:08:43.141Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*TGgj6plzfTWmZOqK" /><figcaption>Photo by <a href="https://unsplash.com/@wideshot?utm_source=medium&amp;utm_medium=referral">Anders Wideskott</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Mobile application development comes often with HTTP requests. We must handle connection with external resources by doing HTTP calls and waiting for responses. When our application is getting bigger, we are using more and more requests. There is always chance that HTTP connection may fail, and we’ll want to know what was wrong.</p><p>If you’re using Flutter, there are many libraries which can handle HTTP requests: <strong>Dio, HttpClient</strong> from <strong>dart:io</strong> package, <strong>http package</strong>. You can use whatever you want in your code. But when we want to inspect these requests, we can only use console logs. Checking hundreds lines of console logs may be painful since HTTP requests/responses can be very long. The better approach to check HTTP calls can be some tool which will show HTTP request on demand, with great formatting. And here comes Alice.</p><h3>Introducing Alice</h3><p><a href="https://github.com/jhomlala/alice">Alice</a> is a <strong>HTTP Inspector</strong> for Flutter. It’s heavily inspired from <a href="https://github.com/jgilfelt/chuck">Chuck tool</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/360/1*XPJHnq_MPydFuzqC2QBEpA.gif" /><figcaption>Alice overview.</figcaption></figure><p>Alice intercepts HTTP requests and stores them. Alice also exposes user interface for inspecting call details. Inspector can be opened from notification (which can be disabled) or manually.</p><p>Alice works best with Dio plugin, because Dio is most advanced HTTP client for Dart language. You can also use Alice with other http clients, which will be described later.</p><p>Alice Inspector refreshes when new requests come in, so list of HTTP calls will be refreshed automatically.</p><h3>Installation</h3><p>First, you need to add dependency to pubspec.yaml:</p><pre>dependencies:<br>  alice: ^&lt;current alice version&gt;</pre><p>You can find current Alice version here: <a href="https://pub.dartlang.org/packages/alice">https://pub.dartlang.org/packages/alice</a></p><p>Second, run command:</p><pre>$ flutter packages get</pre><p>Third, add this import:</p><pre>import &#39;package:alice/alice.dart&#39;;</pre><p>Now you’re ready to use Alice plugin. Let’s create Alice instance:</p><pre>Alice alice = Alice(showNotification: true);</pre><p>If you don’t want to show notification when new request come in, you can set showNotification parameter to false.</p><p>Next thing is navigator key. You must include it in application to open inspector screen. What you need to do is just one line addition in MaterialApp widget (MaterialApp is mostly root widget in your application):</p><pre>navigatorKey: <em>alice</em>.getNavigatorKey()</pre><p>Another example:</p><pre>@override<br>Widget build(BuildContext context) {<br>  return MaterialApp(<br>    navigatorKey: <em>alice</em>.getNavigatorKey(),<br>    ...<br>  );<br>}</pre><p>When you’re using Dio, your configuration will be very simple:</p><pre><em>Dio dio </em>= Dio();<br><em>dio</em>.<em>interceptors</em>.add(<em>alice</em>.getDioInterceptor());</pre><p>Dio has wonderful feature, which allows to add interceptor. Alice exposes special interceptor which must be passed to Dio instance.</p><p>Alice works best with Dio, but there is support for other http clients (not all features of Dio can be handled with these clients).</p><p>When you’re using HttpClient from dart:io package, you need to log every request and response manually:</p><pre><em>httpClient<br>  </em>.postUrl(Uri.parse(&quot;https://jsonplaceholder.typicode.com/posts&quot;))<br>    .then((request) async {<br>  <strong><em>alice</em>.onHttpClientRequest(request, body: body);</strong><br>  request.write(body);<br>  var httpResponse = await request.close();<br>  var responseBody = await httpResponse.transform(utf8.<em>decoder</em>).join();<br>  <strong><em>alice</em>.onHttpClientResponse(httpResponse, request, body: responseBody);</strong><br>});</pre><p>You need to call alice.onHttpClientRequest and alice.onHttpClientResponse to process HTTP calls from HttpClient.</p><p>Lastly, when you’re using http package, you need to pass only response:</p><pre>http.get(&#39;https://jsonplaceholder.typicode.com/posts&#39;).then((response) {<br>  <strong><em>alice</em>.onHttpResponse(response);</strong><br>});</pre><p>You can pass your response by calling alice.onHttpResponse.</p><p>As you can see usage of http package or HttpClient from dart:io package is little bit tricky, so i recommend using Dio for Alice</p><p>Complete example for Alice, can be find here: <a href="https://github.com/jhomlala/alice/blob/master/example/lib/main.dart">https://github.com/jhomlala/alice/blob/master/example/lib/main.dart</a></p><h3>Usage</h3><p>Once you’ve completed installation, you can use Alice to inspect logs. If you’ve enabled notifications, when Alice get first HTTP call, the notification will be displayed. You can click on it to start inspector.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KGgVJBgwm4TIAQi-Qkx7xw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*a9FRefXdgIcN5105ISy_Dg.png" /><figcaption>Notification and inspector UI.</figcaption></figure><p>HTTP Inspector UI has 2 screens. First one, shows list of HTTP calls that has been intercepted by Alice. Each list item contains basic information about HTTP call: method, server, endpoint, time of request, duration, amount of bytes send and received, status code. While your application waits for response, there will be progress indicator shown. Now, if you want to see more details about some HTTP call, click on it. It will navigate you to details screen.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*73773lkrRO7JhaDaUCDgtw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OAjQ7RXNsc_QY82IvBD-Dw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*p7b3M1IkveJWwthpe5MDCg.png" /><figcaption>HTTP call details screen.</figcaption></figure><p>Details screen consists of 4 tabs:</p><ul><li>Overview tab shows general informations of HTTP call (method, server, endpoint, request time, response time, duration, amount of bytes sent/received, client, connection security)</li><li>Request tab shows details of HTTP request (time, amount of bytes sent, content type, request body and headers)</li><li>Response tabs shows details of HTTP response (time, amount of bytes received, status code, response body and headers)</li><li>Error tab shows information about error if any occurred (warning: this tab will show errors only when you’re using Dio plugin)</li></ul><p>When you want to clear all of HTTP calls that was recorded, you can click on trash icon on toolbar.</p><p>What if you don’t want to show notification when new request is being intercepted by Alice? You can turn it off when you’re instantiating Alice (see installation above). But if you’re disabling notification, you need to access somehow inspector UI. In that case you can use this method to open inspector:</p><pre>alice.showInspector();</pre><h3>Summary</h3><p>Alice is a new plugin which helps debugging HTTP calls in your Flutter application. It works best with Dio plugin, but you can use it with other clients too. Alice offers inspector UI, which will show you details of your HTTP calls.</p><p>If you found bug, or want to share your awesome idea, feel free to open issue here:</p><p><a href="https://github.com/jhomlala/alice">jhomlala/alice</a></p><p>I’m always welcome for any contribution, so you can add your awesome idea with PR.</p><p>Thanks for reading.</p><p>P.S. Thanks Alice for your awesomeness :)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9deeddfe8d1" width="1" height="1" alt=""><hr><p><a href="https://medium.com/flutter-community/inspecting-http-requests-in-flutter-9deeddfe8d1">Inspecting HTTP requests in Flutter</a> was originally published in <a href="https://medium.com/flutter-community">Flutter Community</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Handling Flutter errors with Catcher]]></title>
            <link>https://medium.com/flutter-community/handling-flutter-errors-with-catcher-efce74397862?source=rss-4952f6cd91ab------2</link>
            <guid isPermaLink="false">https://medium.com/p/efce74397862</guid>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[dartlang]]></category>
            <category><![CDATA[error-handling]]></category>
            <category><![CDATA[error]]></category>
            <category><![CDATA[dart]]></category>
            <dc:creator><![CDATA[Jakub Homlala]]></dc:creator>
            <pubDate>Sun, 10 Feb 2019 16:40:52 GMT</pubDate>
            <atom:updated>2019-02-14T13:16:04.638Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*OI8fXyoAR0Xuwqme" /><figcaption>Photo by <a href="https://unsplash.com/@chortsang?utm_source=medium&amp;utm_medium=referral">Chor Hung Tsang</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Handling errors is everyday programmer job. This job is done infinite times in daily work. In Dart we can handle it easily with try-catch block. But what will happen if we miss some code? We get for example “red screen of death”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/360/1*xrm7B_c8mE8YVEZi0uY-2A.png" /><figcaption>Typical red screen of death in Flutter.</figcaption></figure><p>It’s okay that our code is not always perfect. Every developer do mistakes, but great developer will fix his mistakes.</p><p>Great thing is being clear with users. When we’re users of some application, we want to know that something unexpected happend and we can decide to send error logs to developer or not. Error logs will help developers patching errors.</p><p>Developers can fix errors, but they need to know what happend and where. In mobile applications development we need tool, that will report invalid application behaviour to the authors. Currently in Flutter we have support for Sentry error tracking and soon to be Firebase Crashlytics support.</p><p>What if we don’t want to use Sentry or Crashlytics? What if we could use some generic tool, which won’t be hard to configure and will help catch errors in developoment stage or even in release? A tool that composes email, so user just need to click “Send” or save crash logs in device memory? And here comes Catcher.</p><h3>Introducing Catcher</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/192/1*TbwWRw0id0pTGIQTHFFTYg.png" /><figcaption>Catcher logo.</figcaption></figure><p>Catcher is new Flutter plugin which catches and handles errors in Flutter application. Catcher offers multiple report modes and handlers to cooperate with Flutter applications. Catcher is heavily inspired from <a href="https://github.com/ACRA/acra">ACRA</a>.</p><p>Catcher report flow is easy to understand (see diagram below). Catcher injects error handlers into your application and catches all unchecked errors. Once it catches error, it creates report and sends it to reporter. Reporter shows information about error and waits for user decision. User can accept or cancel error report. If user accepts error report, then it will be processed by handlers.</p><p>You can report your checked errors which were caught in your try catch block.</p><p>Catcher also collects information about user device hardware and OS. These data can be accessed without any user permission because there’s no personal informations about user. This data will be very helpful, because sometimes error happen because of device errors, not developer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/701/1*rmNfWu-oOHNGkfUC3xBlfA.png" /><figcaption>Diagram which presents how Catcher works.</figcaption></figure><h3>Using Catcher</h3><p>Let’s see a basic example of Catcher. First, we need to install plugin. Go to your pubspec.yaml and add this line:</p><pre>dependencies:<br>  catcher: ^0.0.8</pre><p>Next, you need to run packages get command which downloads Catcher and enables it in your project.</p><p>Last step is adding this import:</p><pre><strong>import </strong>&#39;package:catcher/catcher_plugin.dart&#39;;</pre><p>We are ready to use Catcher. Below you can find basic example of Catcher.</p><pre>main() {<br>  //debug configuration<br>  CatcherOptions debugOptions =<br>      CatcherOptions(DialogReportMode(), [ConsoleHandler()]);<br>  </pre><pre>  //release configuration<br>  CatcherOptions releaseOptions = CatcherOptions(DialogReportMode(), [<br>    EmailManualHandler([&quot;recipient@email.com&quot;])<br>  ]);<br>  </pre><pre>  //profile configuration<br>  CatcherOptions profileOptions = CatcherOptions(<br>    NotificationReportMode(),<br>    [ConsoleHandler(), ToastHandler()],<br>    handlerTimeout: 10000,<br>    customParameters: {&quot;example&quot;: &quot;example_parameter&quot;},<br>  );</pre><pre><br>  //MyApp is root widget<br>  Catcher(MyApp(),<br>      debugConfig: debugOptions,<br>      releaseConfig: releaseOptions,<br>      profileConfig: profileOptions);<br>}</pre><p>Normally, when you run your code, your main has only this line: runApp(MyApp()); , which starts application. When you are using Catcher, you won’t specify this line anymore. Instead of this, you need to create Catcher instance with your root widget instance and application profiles.</p><p>Catcher allows you to configure 3 application profiles: debug , release and profile . In code above we‘re creating 3 instances of CatcherOptions which describes how Catcher will behave in different modes. Catcher will use debugConfig when the application will run in debug environment, releaseConfig when the application will run in release environment and profileConfig when the application run in ‘profile’ mode.</p><p>In each CatcherOptions instance you can configure different report mode and handlers list. <a href="https://github.com/jhomlala/catcher#catcher-configuration">Click here</a> to check all configuration parameters of CatcherOptions .</p><p>There are 4 report modes:</p><ul><li>Silent report mode</li><li>Notification report mode</li><li>Dialog report mode</li><li>Page report mode</li></ul><p>There are 6 handler types:</p><ul><li>Console handler</li><li>Http handler</li><li>File handler</li><li>Toast handler</li><li>Email auto handler</li><li>Email manual handler</li></ul><p>When you’re creating Catcher instance, it will starts your root widget and listens to any errors that happend in your application. Running code above in debug mode, will show dialog once error happens, where user can make decision. Once user accepts report, it will be processed by console handler and that will simply print report in console.</p><p>You can find complete basic example <a href="https://github.com/jhomlala/catcher/blob/master/example/lib/basic_example.dart">here</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0DGnUnysfOtOH-20dN2h1A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GiQd9u0WwHsyvipySZcnDA.png" /><figcaption>Basic example with dialog report mode and console handler. Console shows full report data.</figcaption></figure><h3>Report modes</h3><p>Let’s talk about report modes. As you know report mode is the way how we show information about error to user. Let’s see closer to each report mode.</p><p><strong>Silent report mode </strong>is mode where user don’t take any action. There won’t be any information shown to user about error. User won’t know anything about error, unless some visual handler will show it (for example Toast Handler). Use this report mode if you don’t want to ask users for permissions to handle errors.</p><p><em>Example usage:</em></p><pre>CatcherOptions(SilentReportMode(), [ConsoleHandler()]);</pre><p><strong>Notification report mode</strong> shows user local notification. Once user clicks on it, report will be accepted and processed by configured handlers. To dismiss report, user can remove notification with swipe.</p><p><em>Example usage:</em></p><pre>CatcherOptions(NotificationReportMode(), [ConsoleHandler()]);</pre><p><strong>Dialog report mode</strong> shows user dialog. There are 2 buttons in this dialog: Accept and Cancel. Click on accept button will push log to handlers, cancel will dismiss report.</p><p><em>Example usage:</em></p><pre>CatcherOptions(<br>    DialogReportMode(<br>        titleText: &quot;Title&quot;,<br>        descriptionText: &quot;Description&quot;,<br>        acceptText: &quot;Accept&quot;,<br>        cancelText: &quot;Cancel&quot;),<br>    [ConsoleHandler()]);</pre><p><strong>Page report mode</strong> shows new full screen page with description of error, stack trace and 2 buttons.</p><p><em>Example usage:</em></p><pre>CatcherOptions(<br>    PageReportMode(<br>        titleText: &quot;Title&quot;,<br>        descriptionText: &quot;Description&quot;,<br>        acceptText: &quot;Accept&quot;,<br>        cancelText: &quot;Cancel&quot;,<br>        showStackTrace: <strong>false</strong>),<br>    [ConsoleHandler()]);</pre><p>Dialog report mode and page report mode requires <strong>navigation key</strong> to be configured in your application. This is very easy and you just need to add one line in your MaterialApp widget.</p><pre>@override<br>  Widget build(BuildContext context) {<br>    return MaterialApp(<br>      //********************************************<br>      navigatorKey: Catcher.navigatorKey,<br>      //********************************************<br>      home: Scaffold(<br>          appBar: AppBar(<br>            title: const Text(&#39;Plugin example app&#39;),<br>          ),<br>          body: ChildWidget()),<br>    );<br>  }</pre><p>Report modes have multiple configuration options. You can find all of them <a href="https://github.com/jhomlala/catcher#report-modes">here</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*z8uN9kywTVXfIF93LghD0g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0DGnUnysfOtOH-20dN2h1A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zylTyjZtTh8RA9YJutRGWA.png" /><figcaption>Report modes (from left): Notification, Dialog, Page. Silent report mode doesn’t have any visuals.</figcaption></figure><h3>Handlers</h3><p>Handlers are last element in report flow. They consume report and do something with him. You can configure multiple handlers in each profile, for example Console handler, File Handler. Let’s see every handler closer.</p><p><strong>Console handler</strong> is basic handler. It will print formatted report to console. You can configure console handler to print or not some parts of report.</p><p><em>Example usage:</em></p><pre>CatcherOptions(DialogReportMode(), [<br>  ConsoleHandler(<br>      enableApplicationParameters: <strong>true</strong>,<br>      enableCustomParameters: <strong>true</strong>,<br>      enableStackTrace: <strong>true</strong>,<br>      enableDeviceParameters: <strong>true</strong>)<br>]);</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aVL-9hV23brIZxHJ33ql0A.png" /><figcaption>Console handler.</figcaption></figure><p><strong>File handler</strong> can be used to store logs in user device. You just need to pass file where you want to store your logs.</p><p><em>Example usage:</em></p><pre>Directory externalDir = <strong>await </strong>getExternalStorageDirectory();<br>String path = externalDir.path.toString() + &quot;/log.txt&quot;;<br><br>CatcherOptions debugOptions = CatcherOptions(DialogReportMode(),[FileHandler(File(path))]);<br>CatcherOptions releaseOptions = CatcherOptions(DialogReportMode(),[FileHandler(File(path))]);<br><br>Catcher(MyApp(), debugConfig: debugOptions, releaseConfig: releaseOptions);</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/540/1*dmb-K39EwLpQ940qfVaxEg.jpeg" /><figcaption>File handler logged report in file.</figcaption></figure><p><strong>Http Handler</strong> allows to send data via Http request to server. Currently only Http POST request is supported. You can add custom headers to your request.</p><p><em>Example usage:</em></p><pre>CatcherOptions(DialogReportMode(), [<br>  HttpHandler(HttpRequestType.post, Uri.<em>parse</em>(&quot;http://logs.server.com&quot;),<br>      headers: {&quot;header&quot;: &quot;value&quot;}, requestTimeout: 4000, printLogs: <strong>false</strong>)<br>]);</pre><p>There is simple backend server in Java which exposes endpoint for Catcher reports. You can find implementation <a href="https://github.com/jhomlala/catcher/tree/master/backend">here</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*f5RzSB2mtavNsYYVDRTx6A.png" /><figcaption>Backend server shows collected reports.</figcaption></figure><p><strong>Email auto handler </strong>adds email feature. This handler with automatically send emails to specified email address. You need to specify username and password of your sender email account, so because of this, I recommend use of this handler only during development stage.</p><p><em>Example usage:</em></p><pre>CatcherOptions(DialogReportMode(),<br>    [EmailAutoHandler(<br>        &quot;smtp.gmail.com&quot;, 587, &quot;somefakeemail@gmail.com&quot;, &quot;Catcher&quot;,<br>        &quot;FakePassword&quot;, [&quot;myemail@gmail.com&quot;])<br>    ]);</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fGeEqJ7_4EWrYmddTdhPqQ.png" /><figcaption>Email received from email auto handler.</figcaption></figure><p><strong>Email manual handler</strong> is different from Email auto handler. This handler creates email and opens default email application. User need to take action to send email. You don’t need to specify sender username and password, because user will use their email as a sender, so it’s safe to use in release stage.</p><p><em>Example usage:</em></p><pre>CatcherOptions(DialogReportMode(), [<br>  EmailManualHandler([&quot;email1@email.com&quot;, &quot;email2@email.com&quot;],<br>      enableDeviceParameters: <strong>true</strong>,<br>      enableStackTrace: <strong>true</strong>,<br>      enableCustomParameters: <strong>true</strong>,<br>      enableApplicationParameters: <strong>true</strong>,<br>      sendHtml: <strong>true</strong>,<br>      emailTitle: &quot;Sample Title&quot;,<br>      emailHeader: &quot;Sample Header&quot;,<br>      printLogs: <strong>true</strong>)<br>]);</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/540/1*BjTwcKcop0hcsz2RolfkmQ.jpeg" /><figcaption>Example email composed by Email manual handler..</figcaption></figure><p><strong>Toast handler</strong> is the last handler. It shows toast in user screen. This is great to use when you just need to show short information to user.</p><p><em>Example:</em></p><pre>CatcherOptions(DialogReportMode(), [<br>  ToastHandler(<br>      gravity: ToastHandlerGravity.bottom,<br>      length: ToastHandlerLength.long,<br>      backgroundColor: Colors.<em>red</em>,<br>      textColor: Colors.<em>white</em>,<br>      textSize: 12.0,<br>      customMessage: &quot;We are sorry but unexpected error occured.&quot;)<br>]);</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/360/1*AtHBgGJY_pjxQSEw8XarhA.png" /><figcaption>Toast handler shows toast with information about error.</figcaption></figure><p>Description of all handler and their options can be find <a href="https://github.com/jhomlala/catcher/blob/master/README.md#handlers">here</a>.</p><p>You can even define your own handler! Just create new class and extend ReportHandler class:</p><pre><strong>import </strong>&#39;package:catcher/catcher_plugin.dart&#39;;<br><br><strong>class </strong>MyHandler <strong>extends </strong>ReportHandler{<br>  @override<br>  Future&lt;bool&gt; handle(Report error) <strong>async</strong>{<br>    //my implementation<br>    <strong>return true</strong>;<br>  }<br>  <br>}</pre><h3><strong>Conclusion</strong></h3><p>Catcher is new plugin but yet powerful. Plugin is still in development, but you can use it in your project. It’s pretty easy and straightforward to implement it in your project, so give a try!</p><p>Feel free to report issues or provide feedback in Github. You are also welcome to add new features to Catcher. Feel free to contribute to this project!</p><p><a href="https://github.com/jhomlala/catcher">jhomlala/catcher</a></p><p>Thanks for reading!</p><p><a href="https://twitter.com/FlutterComm">JavaScript is not available.</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=efce74397862" width="1" height="1" alt=""><hr><p><a href="https://medium.com/flutter-community/handling-flutter-errors-with-catcher-efce74397862">Handling Flutter errors with Catcher</a> was originally published in <a href="https://medium.com/flutter-community">Flutter Community</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Flutter HTTP requests with Dio, RxDart and Bloc]]></title>
            <link>https://medium.com/@jhomlala/flutter-http-requests-with-dio-rxdart-and-bloc-da325ca5fe33?source=rss-4952f6cd91ab------2</link>
            <guid isPermaLink="false">https://medium.com/p/da325ca5fe33</guid>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[dart]]></category>
            <category><![CDATA[dios]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[Jakub Homlala]]></dc:creator>
            <pubDate>Sun, 27 Jan 2019 11:19:06 GMT</pubDate>
            <atom:updated>2020-06-26T16:17:34.657Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*EN-q4TWUQ5CfhadX" /><figcaption>Photo by <a href="https://unsplash.com/@maunzo?utm_source=medium&amp;utm_medium=referral">Mike Aunzo</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Making HTTP requests in mobile application is one of the common tasks. Thanks to http requests, application can communicate with backend and selects data.</p><p>Flutter framework offers <a href="https://pub.dartlang.org/packages/http">http package</a> which works great when we need do basic stuff. When we need to do something more advanced, we need something bigger. And this can be done by using Dio. Dio is http connection library which has extra features like interceptors that will be helpful in many tasks (adding token authentication for each request, logging requests). Dio API is pretty easy and the library is being maintained by the authors. It’s really worth trying.</p><p>Todays modern mobile development hot topic is reactive paradigm. Almost every application uses reactive paradigm, which is great. RxDart package offers support for basic reactive things just like Subjects or Observables. For our project it will more than enough.</p><p>Managing widget/application state is open topic in Flutter. There are many implementations like Bloc and Redux(2020 update: Provider is also worth mentioning here. Check whole list <a href="https://flutter.dev/docs/development/data-and-backend/state-mgmt/options">here</a>). In this article we will use Bloc pattern which is pretty simple and powerful. You can use whatever you want in your project.</p><p>In this article i’m going to show you how to work with Dio, RxDart and Bloc to create basic application which loads data from external resource and show it in application.</p><h4>Data</h4><p>We’re going to use random user API which generates fake user data and returns it in JSON format. For that case we will use <a href="http://randomuser.me">http://randomuser.me API</a>. The random user service accepts request on this endpoint:</p><blockquote><em>Want to read this story later? Save it in </em><a href="https://usejournal.com/?utm_source=medium.com&amp;utm_medium=blog&amp;utm_campaign=noteworthy&amp;utm_content=eid7"><em>Journal</em></a><em>.</em></blockquote><pre>GET <a href="https://randomuser.me/api/">https://randomuser.me/api/</a></pre><p>The result of this endpoint is this JSON:</p><pre>{  <br>   <strong>&quot;results&quot;</strong>:[  <br>      {  <br>         <strong>&quot;gender&quot;</strong>:&quot;male&quot;,<br>         <strong>&quot;name&quot;</strong>:{  <br>            <strong>&quot;title&quot;</strong>:&quot;mr&quot;,<br>            <strong>&quot;first&quot;</strong>:&quot;william&quot;,<br>            <strong>&quot;last&quot;</strong>:&quot;brar&quot;<br>         },<br>         <strong>&quot;location&quot;</strong>:{  <br>            <strong>&quot;street&quot;</strong>:&quot;8335 stanley way&quot;,<br>            <strong>&quot;city&quot;</strong>:&quot;lloydminster&quot;,<br>            <strong>&quot;state&quot;</strong>:&quot;northwest territories&quot;,<br>            <strong>&quot;postcode&quot;</strong>:&quot;Q4D 8S9&quot;,<br>            <strong>&quot;coordinates&quot;</strong>:{  <br>               <strong>&quot;latitude&quot;</strong>:&quot;-62.1569&quot;,<br>               <strong>&quot;longitude&quot;</strong>:&quot;-79.5201&quot;<br>            },<br>            <strong>&quot;timezone&quot;</strong>:{  <br>               <strong>&quot;offset&quot;</strong>:&quot;-11:00&quot;,<br>               <strong>&quot;description&quot;</strong>:&quot;Midway Island, Samoa&quot;<br>            }<br>         },<br>         <strong>&quot;email&quot;</strong>:&quot;william.brar@example.com&quot;,<br>         <strong>&quot;login&quot;</strong>:{  <br>            <strong>&quot;uuid&quot;</strong>:&quot;f10dd86d-3297-4e4c-a33d-65d009740057&quot;,<br>            <strong>&quot;username&quot;</strong>:&quot;brownsnake466&quot;,<br>            <strong>&quot;password&quot;</strong>:&quot;citroen&quot;,<br>            <strong>&quot;salt&quot;</strong>:&quot;ojLC7qVQ&quot;,<br>            <strong>&quot;md5&quot;</strong>:&quot;8f30c2d6382c9290c979cf585b15cca3&quot;,<br>            <strong>&quot;sha1&quot;</strong>:&quot;57ded975628df8205d1d9b6eb11b0ffa7baafa43&quot;,<br>            <strong>&quot;sha256&quot;</strong>:&quot;b8f87131af64ee9c315411dadb499c8d10bdf00985bcd4b4a52c32b76ba84f9c&quot;<br>         },<br>         <strong>&quot;dob&quot;</strong>:{  <br>            <strong>&quot;date&quot;</strong>:&quot;1961-07-24T23:24:35Z&quot;,<br>            <strong>&quot;age&quot;</strong>:57<br>         },<br>         <strong>&quot;registered&quot;</strong>:{  <br>            <strong>&quot;date&quot;</strong>:&quot;2015-06-04T07:15:15Z&quot;,<br>            <strong>&quot;age&quot;</strong>:3<br>         },<br>         <strong>&quot;phone&quot;</strong>:&quot;064-200-3481&quot;,<br>         <strong>&quot;cell&quot;</strong>:&quot;790-556-8085&quot;,<br>         <strong>&quot;id&quot;</strong>:{  <br>            <strong>&quot;name&quot;</strong>:&quot;&quot;,<br>            <strong>&quot;value&quot;</strong>:null<br>         },<br>         <strong>&quot;picture&quot;</strong>:{  <br>            <strong>&quot;large&quot;</strong>:&quot;https://randomuser.me/api/portraits/men/1.jpg&quot;,<br>            <strong>&quot;medium&quot;</strong>:&quot;https://randomuser.me/api/portraits/med/men/1.jpg&quot;,<br>            <strong>&quot;thumbnail&quot;</strong>:&quot;https://randomuser.me/api/portraits/thumb/men/1.jpg&quot;<br>         },<br>         <strong>&quot;nat&quot;</strong>:&quot;CA&quot;<br>      }<br>   ],<br>   <strong>&quot;info&quot;</strong>:{  <br>      <strong>&quot;seed&quot;</strong>:&quot;7ea59fb8e50ce24f&quot;,<br>      <strong>&quot;results&quot;</strong>:1,<br>      <strong>&quot;page&quot;</strong>:1,<br>      <strong>&quot;version&quot;</strong>:&quot;1.2&quot;<br>   }<br>}</pre><h4>Installation</h4><p>To install Dio package, we need go to file pubspec.yamlinside Flutter project and add this line:</p><pre><strong>dio</strong>: ^3.0.8</pre><p>^3.0.8 notation means that we are accepting 3.0.x versions of Dio, where x≥8 .</p><p>(You can check current Dio version here: <a href="https://pub.dartlang.org/packages/dio">https://pub.dartlang.org/packages/dio</a>)</p><p>In our project we also need RxDart, so let’s add it:</p><pre><strong>rxdart: ^0.23.1</strong></pre><p>Now we are ready to run flutter packages get command. This command downloads packages and enable them in project.</p><h4>Model</h4><p>First step is model. We need to create class structures which correspond to JSON response from API. We don’t need to map all the data from API, since it won’t be useful. Let’s create these classes:</p><pre><strong>class </strong>Location {<br>  <strong>final </strong>String street;<br>  <strong>final </strong>String city;<br>  <strong>final </strong>String state;<br><br><br>  Location(<strong>this</strong>.street, <strong>this</strong>.city, <strong>this</strong>.state);<br><br>  Location.fromJson(Map&lt;String, <strong>dynamic</strong>&gt; json)<br>      : street = json[&quot;street&quot;],<br>        city = json[&quot;city&quot;],<br>        state = json[&quot;state&quot;];<br>}</pre><pre><strong>class </strong>Name {<br>  <strong>final </strong>String title;<br>  <strong>final </strong>String first;<br>  <strong>final </strong>String last;<br><br>  Name(<strong>this</strong>.title, <strong>this</strong>.first, <strong>this</strong>.last);<br><br>  Name.fromJson(Map&lt;String, <strong>dynamic</strong>&gt; json)<br>      : title = json[&quot;title&quot;],<br>        first = json[&quot;first&quot;],<br>        last = json[&quot;last&quot;];<br>}<br></pre><pre><strong>class </strong>Picture {<br>  <strong>final </strong>String large;<br>  <strong>final </strong>String medium;<br>  <strong>final </strong>String thumbnail;<br><br>  Picture(<strong>this</strong>.large, <strong>this</strong>.medium, <strong>this</strong>.thumbnail);<br><br>  Picture.fromJson(Map&lt;String, <strong>dynamic</strong>&gt; json)<br>      : large = json[&quot;large&quot;],<br>        medium = json[&quot;medium&quot;],<br>        thumbnail = json[&quot;thumbnail&quot;];<br>}<br></pre><pre><strong>import </strong>&#39;package:user/model/location.dart&#39;;<br><strong>import </strong>&#39;package:user/model/name.dart&#39;;<br><strong>import </strong>&#39;package:user/model/picture.dart&#39;;<br><br><strong>class </strong>User {<br>  <strong>final </strong>String gender;<br>  <strong>final </strong>Name name;<br>  <strong>final </strong>Location location;<br>  <strong>final </strong>String email;<br>  <strong>final </strong>Picture picture;<br><br>  User(<strong>this</strong>.gender, <strong>this</strong>.name, <strong>this</strong>.location, <strong>this</strong>.email, <strong>this</strong>.picture);<br><br>  User.fromJson(Map&lt;String, <strong>dynamic</strong>&gt; json)<br>      : gender = json[&quot;gender&quot;],<br>        name = Name.fromJson(json[&quot;name&quot;]),<br>        location = Location.fromJson(json[&quot;location&quot;]),<br>        email = json[&quot;email&quot;],<br>        picture = Picture.fromJson(json[&quot;picture&quot;]);<br><br>}</pre><pre><strong>import </strong>&#39;package:user/model/user.dart&#39;;<br><br><strong>class </strong>UserResponse {<br>  <strong>final </strong>List&lt;User&gt; results;<br>  <strong>final </strong>String error;<br><br>  UserResponse(<strong>this</strong>.results, <strong>this</strong>.error);<br><br>  UserResponse.fromJson(Map&lt;String, <strong>dynamic</strong>&gt; json)<br>      : results =<br>            (json[&quot;results&quot;] <strong>as </strong>List).map((i) =&gt; <strong>new </strong>User.fromJson(i)).toList(),<br>        error = &quot;&quot;;<br><br>  UserResponse.withError(String errorValue)<br>      : results = List(),<br>        error = errorValue;<br>}</pre><p>Each class contains final fields and constructors (final fields must be initiated in construction part). You can find special named constructor &lt;class&gt;.fromJsonwhich constructs class from Map&lt;String,dynamic&gt;. This map will be created by Dio from endpoint response. When we init list field in UserResponse we need to setup it more complex way, which includes projection to List and map function which maps each row to User class. The UserResponse class has additional parameter error which is not being returned from API. This field will be helpful when we need to store information about any error that happend in connection process. Because of this, we need to add additional named constructor which handles the error situation and this constructor is UserResponse.withError .</p><h4>API Provider</h4><p>Since we have our model ready, we can create code which connects to endpoint and gets response.</p><pre><strong>import </strong>&#39;package:user/model/user_response.dart&#39;;<br><strong>import </strong>&#39;package:dio/dio.dart&#39;;<br><br><strong>class </strong>UserApiProvider{<br>  <strong>final </strong>String _endpoint = &quot;https://randomuser.me/api/&quot;;<br>  <strong>final </strong>Dio _dio = Dio();<br><br>  Future&lt;UserResponse&gt; getUser() <strong>async </strong>{<br>    <strong>try </strong>{<br>      Response response = <strong>await </strong>_dio.get(_endpoint);<br>      <strong>return </strong>UserResponse.fromJson(response.data);<br>    } <strong>catch </strong>(error, stacktrace) {<br>      print(&quot;Exception occured: $error stackTrace: $stacktrace&quot;);<br>      <strong>return </strong>UserResponse.withError(&quot;$error&quot;);<br>    }<br>  }<br>}</pre><p>UserApiProvider class contains only one method getUser which connects to endpoints and returns UserResponse . The method is asynchronous, thus the return is Future&lt;UserResponse&gt; .</p><h4>Repository</h4><p>The repository class will mediate between high level components of our architecture (like bloc-s) and UserApiProvider . The UserRepository class will be repository for our random user selected from API.</p><pre><strong>import </strong>&#39;package:user/model/user_response.dart&#39;;<br><strong>import </strong>&#39;package:user/repository/user_api_provider.dart&#39;;<br><br><strong>class </strong>UserRepository{<br>  UserApiProvider _apiProvider = UserApiProvider();<br><br>  Future&lt;UserResponse&gt; getUser(){<br>    <strong>return </strong>_apiProvider.getUser();<br>  }<br>}</pre><h4>Bloc</h4><p>Let’s add high level component of our architecture which is bloc (business logic component — read about it <a href="https://medium.com/flutterpub/architecting-your-flutter-project-bd04e144a8f1">here</a>).</p><p>UserBloc is the only component which can be used from UI class (in terms of clean architecture). UserBloc fetches data from repository and publish it via Rx subjects.</p><pre><strong>import </strong>&#39;package:user/model/user_response.dart&#39;;<br><strong>import </strong>&#39;package:user/repository/user_repository.dart&#39;;<br><strong>import </strong>&#39;package:rxdart/rxdart.dart&#39;;<br><br><strong>class </strong>UserBloc {<br>  <strong>final </strong>UserRepository _repository = UserRepository();<br>  <strong>final </strong>BehaviorSubject&lt;UserResponse&gt; _subject =<br>      BehaviorSubject&lt;UserResponse&gt;();<br><br>  getUser() <strong>async </strong>{<br>    UserResponse response = <strong>await </strong>_repository.getUser();<br>    _subject.sink.add(response);<br>  }<br><br>  dispose() {<br>    _subject.close();<br>  }<br><br>  BehaviorSubject&lt;UserResponse&gt; <strong>get </strong>subject =&gt; _subject;<br>  <br>}<br><strong>final </strong>bloc = UserBloc();</pre><p>getUser method gets data from repository and publish them in _subject .</p><p>BehaviorSubject is subject which returns last emitted value when new observer joins. This can be helpful when our widget will change his state. The observer will be our widget which will show user data.</p><p>dispose method should be called, when UserBloc will be no longer used.</p><h4>Widget</h4><p>Widget which will display user data will be build around StreamBuilder component. There will be 3 states:</p><ol><li>Loading</li><li>Error</li><li>Success</li></ol><p>Loading state is default one. It will shows progress indicator and loading text. Error state can happen when connection with API fails (for example when user goes offline). Success is state when data was loaded sucessfully.</p><p>StreamBuilder has 2 important parameters: stream which is our source of data (the component will change his state when something new has pushed through stream) and builder which allows to create child widget based on current state.</p><pre><strong>import </strong>&#39;package:flutter/material.dart&#39;;<br><strong>import </strong>&#39;package:flutter/widgets.dart&#39;;<br><strong>import </strong>&#39;package:user/bloc/user_bloc.dart&#39;;<br><strong>import </strong>&#39;package:user/model/user_response.dart&#39;;<br><br><strong>class </strong>UserWidget <strong>extends </strong>StatefulWidget {<br>  @override<br>  State&lt;StatefulWidget&gt; createState() {<br>    <strong>return </strong>_UserWidgetState();<br>  }<br>}<br><br><strong>class </strong>_UserWidgetState <strong>extends </strong>State&lt;UserWidget&gt; {<br><br>@override<br>  <strong>void </strong>initState() {<br>    <strong>super</strong>.initState();<br>    bloc.getUser();<br>  }<br><br>  @override<br>  Widget build(BuildContext context) {<br>    <strong>return </strong>StreamBuilder&lt;UserResponse&gt;(<br>      stream: bloc.subject.stream,<br>      builder: (context, AsyncSnapshot&lt;UserResponse&gt; snapshot) {<br>        <strong>if </strong>(snapshot.hasData) {<br>          <strong>if </strong>(snapshot.data.error != <strong>null </strong>&amp;&amp; snapshot.data.error.length &gt; 0){<br>            <strong>return </strong>_buildErrorWidget(snapshot.data.error);<br>          }<br>          <strong>return </strong>_buildUserWidget(snapshot.data);<br><br>        } <strong>else if </strong>(snapshot.hasError) {<br>          <strong>return </strong>_buildErrorWidget(snapshot.error);<br>        } <strong>else </strong>{<br>          <strong>return </strong>_buildLoadingWidget();<br>        }<br>      },<br>    );<br>  }<br><br>  Widget _buildLoadingWidget() {<br>    <strong>return </strong>Center(<br>        child: Column(<br>      mainAxisAlignment: MainAxisAlignment.center,<br>      children: [Text(&quot;Loading data from API...&quot;), CircularProgressIndicator()],<br>    ));<br>  }<br><br>  Widget _buildErrorWidget(String error) {<br>    <strong>return </strong>Center(<br>        child: Column(<br>      mainAxisAlignment: MainAxisAlignment.center,<br>      children: [<br>        Text(&quot;Error occured: $error&quot;),<br>      ],<br>    ));<br>  }<br><br>  Widget _buildUserWidget(UserResponse data) {<br>    <strong>return </strong>Center(<br>        child: Column(<br>          mainAxisAlignment: MainAxisAlignment.center,<br>          children: [<br>            Text(&quot;User widget&quot;),<br>          ],<br>        ));<br>  }<br>}</pre><p>_UserWidgetState is class which implements UserWidget state. In initState we inform bloc that is time to load data. In Streambuilder’s builderparameter we decide what should be displayed at this moment. When there is no data yet, we will show loading widget which is build by _buildLoadingWidget method. Once error occured, we’ll show error widget build by _buildErrorWidget method and when the data was sucessfully returned, we’ll use _buildUserWidget .</p><h4>Beautify UI</h4><p>Our mockup from previous point shows only Text widget. It’s time to build something more user friendly.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jxAktgJksKItlG58qU6KfA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FGzkHhp3c1AKZcOmfEUKxg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rg6kbJR6pWEkerohTbiJJg.png" /></figure><p>Firstly, there was added background gradient. This was done by wrapping UserWidget with Container widget which has BoxDecoration with LinearGradient .</p><pre>Container(<br>  child: UserWidget(),<br>  decoration: BoxDecoration(<br>    gradient: LinearGradient(<br>      begin: Alignment.<em>topCenter</em>,<br>      end: Alignment.<em>bottomCenter</em>,<br>      stops: [0.0, 0.7],<br>      colors: [<br>        Color(0xFFF12711),<br>        Color(0xFFf5af19),<br>      ],<br>    ),<br>  ),<br>)</pre><p>Secondly, we will declare ThemeData which contains Text styles.</p><pre>MaterialApp(<br>  title: &#39;Flutter Demo&#39;,<br>  theme: ThemeData(<br>    primaryColor: Colors.<em>white</em>,<br>    textTheme: TextTheme(<br>        title: TextStyle(fontSize: 30, color: Colors.<em>white</em>),<br>        subtitle: TextStyle(fontSize: 20, color: Colors.<em>white</em>),<br>        body1: TextStyle(fontSize: 15, color: Colors.<em>white</em>)),<br>  ),<br>  home: MyHomePage(),<br>);</pre><p>Declaring theme data keeps our <strong>code cleaner</strong>, because we don’t need to specify in each Text component style data.</p><p>Next, it’s time to modify loading, error and success widgets. Loading and error widgets has received style parameter:</p><pre>Text(&quot;Loading data from API...&quot;,<br>    style: Theme.<em>of</em>(context).textTheme.subtitle),</pre><p>Success widget has been modified much more. It has now CircleAvatar and multiple Text widgets which display user data. Please note that between Text components we’ve added Padding widgets to create space in layout.</p><pre>Widget _buildUserWidget(UserResponse data) {<br>  User user = data.results[0];<br>  <strong>return </strong>Center(<br>      child: Column(<br>    mainAxisAlignment: MainAxisAlignment.center,<br>    children: [<br>      CircleAvatar(<br>        radius: 70,<br>        backgroundImage: NetworkImage(user.picture.large),<br>      ),<br>      Text(<br>        &quot;${_capitalizeFirstLetter(user.name.first)} ${_capitalizeFirstLetter(user.name.last)}&quot;,<br>        style: Theme.<em>of</em>(context).textTheme.title,<br>      ),<br>      Text(user.email, style: Theme.<em>of</em>(context).textTheme.subtitle),<br>      Padding(<br>        padding: EdgeInsets.only(top: 5),<br>      ),<br>      Text(user.location.street, style: Theme.<em>of</em>(context).textTheme.body1),<br>      Padding(<br>        padding: EdgeInsets.only(top: 5),<br>      ),<br>      Text(user.location.city, style: Theme.<em>of</em>(context).textTheme.body1),<br>      Padding(<br>        padding: EdgeInsets.only(top: 5),<br>      ),<br>      Text(user.location.state, style: Theme.<em>of</em>(context).textTheme.body1),<br>    ],<br>  ));<br>}</pre><h4>Handle connection error</h4><p>When we’re reaching external resources like API, many problems may happend. One of the most common error is SocketException which happens when we’re offline. In our code we have try catch block, and our application will be not killed when this error occurs. Unfortunately, user will see this error in his screen:</p><blockquote>Exception occured: DioError [DioErrorType.DEFAULT]: SocketException: Failed host lookup: ‘randomuser.me’ (OS Error: No address associated with hostname, errno = 7)</blockquote><p>Lets create _handleError method which will translate DioError into human readable message.</p><pre>String _handleError(Error error) {<br>    String errorDescription = &quot;&quot;;<br>    if (error is DioError) {<br>      DioError dioError = error as DioError;<br>      switch (dioError.<em>type</em>) {<br>        case DioErrorType.<em>CANCEL</em>:<br>          errorDescription = &quot;Request to API server was cancelled&quot;;<br>          break;<br>        case DioErrorType.<em>CONNECT_TIMEOUT</em>:<br>          errorDescription = &quot;Connection timeout with API server&quot;;<br>          break;<br>        case DioErrorType.<em>DEFAULT</em>:<br>          errorDescription =<br>              &quot;Connection to API server failed due to internet connection&quot;;<br>          break;<br>        case DioErrorType.<em>RECEIVE_TIMEOUT</em>:<br>          errorDescription = &quot;Receive timeout in connection with API server&quot;;<br>          break;<br>        case DioErrorType.<em>RESPONSE</em>:<br>          errorDescription =<br>              &quot;Received invalid status code: ${dioError.<em>response</em>.<em>statusCode</em>}&quot;;<br>          break;<br>        case DioErrorType.<em>SEND_TIMEOUT</em>:<br>          errorDescription = &quot;Send timeout in connection with API server&quot;;<br>          break;<br>      }<br>    } else {<br>      errorDescription = &quot;Unexpected error occured&quot;;<br>    }<br>    return errorDescription;<br>  }<br>}</pre><p>Dio providers multiple error types which can be handled by us. DioError has response and requestobject, which can be used to show for example invalid status code.</p><p>Then we can use _handleError method in catch block:</p><pre>...<br>} <strong>catch </strong>(error, stacktrace) {<br>  print(&quot;Exception occured: $error stackTrace: $stacktrace&quot;);<br>  <strong>return </strong>UserResponse.withError(_handleError(error));<br>}</pre><h4>Modify Dio settings</h4><p>When we want change Dio default behavior just like connection timeout time we can use Options in Dio constructor.</p><pre>Dio _dio;<br><br>UserApiProvider() {<br>  BaseOptions options =<br>      BaseOptions(receiveTimeout: 5000, connectTimeout: 5000);<br>  <em>_dio </em>= Dio(options);<br>}</pre><p>We have added default constructor for UserApiProvider class. In this constructor we need to create BaseOptions instance and specify parameters that we want to change. To see all parameters that can be changed, click <a href="https://pub.dev/documentation/dio/latest/dio/BaseOptions-class.html">here</a>. When our BaseOptions instance is ready, we can pass it to Dio constructor.</p><h4>Intercept Dio requests</h4><p>One of the common problem is adding some behavior for each request/response that is being made. We can add code for each method in API provider classes, but this will be useless since Dio provides interceptors.</p><p>Let’s create interceptor which logs each response/request just like <a href="https://github.com/square/okhttp/blob/master/okhttp-logging-interceptor/src/main/java/okhttp3/logging/HttpLoggingInterceptor.java#L52">OkHttp logging interceptor style</a>.</p><p>Dio instance has interceptors field where we can add our log interceptor. Code from our interceptor will be called before request and after response.</p><pre>class LoggingInterceptor extends Interceptor{<br><br>  int <em>_maxCharactersPerLine </em>= 200;<br><br>  @override<br>  Future onRequest(RequestOptions options) {<br>    print(&quot;--&gt; ${options.<em>method</em>} ${options.<em>path</em>}&quot;);<br>    print(&quot;Content type: ${options.<em>contentType</em>}&quot;);<br>    print(&quot;&lt;-- END HTTP&quot;);<br>    return super.onRequest(options);<br>  }<br><br>  @override<br>  Future onResponse(Response response) {<br>    print(<br>        &quot;&lt;-- ${response.<em>statusCode</em>} ${response.<em>request</em>.<em>method</em>} ${response.<em>request</em>.<em>path</em>}&quot;);<br>    String responseAsString = response.<em>data</em>.toString();<br>    if (responseAsString.<em>length </em>&gt; <em>_maxCharactersPerLine</em>) {<br>      int iterations =<br>      (responseAsString.<em>length </em>/ <em>_maxCharactersPerLine</em>).floor();<br>      for (int i = 0; i &lt;= iterations; i++) {<br>        int endingIndex = i * <em>_maxCharactersPerLine </em>+ <em>_maxCharactersPerLine</em>;<br>        if (endingIndex &gt; responseAsString.<em>length</em>) {<br>          endingIndex = responseAsString.<em>length</em>;<br>        }<br>        print(responseAsString.substring(<br>            i * <em>_maxCharactersPerLine</em>, endingIndex));<br>      }<br>    } else {<br>      print(response.<em>data</em>);<br>    }<br>    print(&quot;&lt;-- END HTTP&quot;);<br><br>    return super.onResponse(response);<br>  }<br><br>  @override<br>  Future onError(DioError err) {<br>    print(&quot;&lt;-- Error --&gt;&quot;);<br>    print(err.<em>error</em>);<br>    print(err.<em>message</em>);<br>    return super.onError(err);<br>  }<br><br>}</pre><p>Here is our LoggingInterceptor class. It’s extends Interceptor class provided by Dio. We need to override onRequest , onRespone and onError . We are adding our behaviour (which is just print to console) in these methods.</p><p>Very often returned response is very long and doesn’t fit console view. We need to split response longer than maxCharacterPerLine into multiple lines, so it will fit inside console. Our logger will print response output in multiple lines.</p><p>Final step: let’s add our interceptor to Dio:</p><pre><em>_dio</em>.<em>interceptors</em>.add(LoggingInterceptor());</pre><p>After application restart, you should see logs in console:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*p7OlNKCI0xkXLnkWe62EcA.png" /></figure><h4>End</h4><p>That’s all for the basic stuff. You can check code for this project here:</p><p><a href="https://github.com/jhomlala/randomuser">jhomlala/randomuser</a></p><p>Feel free to visit my open source weather application build in Flutter:</p><p><a href="https://github.com/jhomlala/feather">jhomlala/feather</a></p><p>Thanks for reading!</p><p>Jan 2020: Revisited article and updated stuff!</p><figure><a href="https://usejournal.com/?utm_source=medium.com&amp;utm_medium=noteworthy_blog&amp;utm_campaign=tech&amp;utm_content=guest_post_image"><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PYxBP_WV7LI6zc-zdUkNqQ.png" /></a></figure><p>📝 Read this story later in <a href="https://usejournal.com/?utm_source=medium.com&amp;utm_medium=noteworthy_blog&amp;utm_campaign=tech&amp;utm_content=guest_post_read_later_text">Journal</a>.</p><p>👩‍💻 Wake up every Sunday morning to the week’s most noteworthy stories in Tech waiting in your inbox. <a href="https://usejournal.com/newsletter/noteworthy-in-tech/?utm_source=medium.com&amp;utm_medium=noteworthy_blog&amp;utm_campaign=tech&amp;utm_content=guest_post_text">Read the Noteworthy in Tech newsletter</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=da325ca5fe33" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>