[{"content":"Introduction Many developers want to monetize their apps to cover development costs. One common approach is a subscription-based app model. In this article, I am going to show you how to enable test purchases for your Android apps to easily test your subscriptions and digital products.\nWhat you need To get started, you need a working Android app in the internal test track and test users.\nTo allow test purchases for your test users, go to the Play Console -\u0026gt; Settings -\u0026gt; License testing.\nSetting up test purchases for Android Apps in Google Play Console\nChoose the email list of the testers, you want to grant access to test purchases. Be aware that this is across all apps the tester has access to!\nIt might be reasonable to create at least one list of testers per app.\nDon\u0026rsquo;t forget to save the changes. They are effective immediately.\nSetting up test purchases for Android Apps in Google Play Console\n💡 Tip\nProducts are permanent while subscriptions auto-renew after a short period of time (a yearly subscription renews every 30 minutes). Users can cancel their subscriptions in their Google Play account.\nWhy it might not work You must have configured a digital product (one time or subscription) for your app.\nYour APK/App Bundle must be uploaded to Play Store in a production or test track.\nYour app must have testers or users that are enabled to make test purchases.\nThe app must run on a real device. It doesn\u0026rsquo;t work in emulators.\nRead the official documentation for more details!\nConclusion Setting up and testing digital product and subscriptions is an essential part in monetizing mobile apps. In this article, you learned how to enable test purchase for your Android apps. Publish your test app, configure test users, and enable license testing. That\u0026rsquo;s it!\n","permalink":"https://quickcoder.org/posts/how-to-enable-test-purchases-for-your-android-apps/","summary":"\u003ch3 id=\"introduction\"\u003eIntroduction\u003c/h3\u003e\n\u003cp\u003eMany developers want to monetize their apps to cover development costs. One common approach is a subscription-based app model. In this article, I am going to show you how to enable test purchases for your Android apps to easily test your subscriptions and digital products.\u003c/p\u003e\n\u003ch3 id=\"what-you-need\"\u003eWhat you need\u003c/h3\u003e\n\u003cp\u003eTo get started, you need a working Android app in the internal test track and test users.\u003c/p\u003e\n\u003cp\u003eTo allow test purchases for your test users, go to the \u003cstrong\u003ePlay Console\u003c/strong\u003e -\u0026gt; \u003cstrong\u003eSettings\u003c/strong\u003e -\u0026gt; \u003cstrong\u003eLicense testing\u003c/strong\u003e.\u003c/p\u003e","title":"How To Enable Test Purchases For Your Android Apps"},{"content":"Introduction A few days ago, I started noticing that Copilot was a co-author in some of my commits to my GitHub project. Since I write commit messages myself, I had no idea what this was about. The hint in GitHub just tells\nCo-authored-by: Copilot \u0026lt;copilot@github.com\u0026gt; Yes, I am using GitHub Copilot for development. But I haven’t seen any information that it will be mentioned in my commits as co-author. It just appeared one day.\nMy recent commit history on GitHub with Copilot being added as co-author.\nThen, I stumbled across an article explaining the details. It’s a VS Code setting and it was changed recently. When you use VS Code’s internal Git tooling and your commit contains AI edits or a chat session, Copilot will be a co-author in this commit. This does not happen if you use external Git tooling outside of VS Code.\nTo disable this behavior, go to File \u0026gt; Preferences \u0026gt; Settings, then type git.addAICoAuthor in the search box, and change the setting to off.\nVS Code setting to remove Copilot as co-author in commit messages.\nAfter that, the original behavior is restored and Copilot won’t appear in your commit messages again. However, it won’t affect existing commits.\nConclusion An annoying move by Microsoft to silently add Copilot as a co-author in your commits when using VS Code. It would have been nice to get a heads up before the change. I would have changed the setting immediately if I had known…\n","permalink":"https://quickcoder.org/posts/copilot-in-github-commit-messages/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eA few days ago, I started noticing that Copilot was a co-author in some of my commits to my GitHub project. Since I write commit messages myself, I had no idea what this was about. The hint in GitHub just tells\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eCo-authored-by\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eCopilot \u0026lt;copilot@github.com\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYes, I am using GitHub Copilot for development. But I haven’t seen any information that it will be mentioned in my commits as co-author. It just appeared one day.\u003c/p\u003e","title":"Why Does Copilot Suddenly Appear In GitHub Commit Messages?!"},{"content":"Introduction Windows 11 is forcing us to use AI, ads, and many other features most people don’t want. Here is how you disable AI in Windows.\nIn recent years, Microsoft changed Windows from a local operating system to an always-online, cloud-based, agentic one full of AI slop, ads, and unwanted features.\nHere are some of the feature that nobody asked for and nobody wants:\nWindows Recall Windows Copilot Start menu searches the web Ads everywhere Excessive telemetry information … I have been using Windows for years and it wasn’t a problem until recently. Due to some app constraints, I cannot switch to Linux and I am also not a big fan of virtual machines or dual boot solutions. But Microsoft basically forced me to do something. I started hacking the registry and try out programs and scripts to make Windows a bit to what it used to be:\nA productive working and gaming environment\nHere is what I tried out to tame Windows. I hope it helps you too!\nO\u0026amp;O ShutUp10 O\u0026amp;O ShutUp10 is a graphical tool with countless options to customize windows. The settings are grouped by topics like privacy, Microsoft Office, Windows AI, or Microsoft Edge. You can apply the changes for the current user or the entire machine. Just enable or disable a switch and the tool does the rest.\nIt shows you recommended settings, describes all settings in detail with possible consequences, and creates a restore points before making any changes.\nUnder the hood, the tool modifies registry keys. It’s a handy shortcut if you don’t want to deal with all the settings yourself.\nO\u0026amp;O ShutUp user interface\n✅ Free to use and highly recommended!\nCrapFixer CrapFixer looks like XP-Antispy or CCleaner (the older generation might remember those tools), has many options, does a good job (can even disable Windows Recall), and is under active development.\nIt is kind of the same like O\u0026amp;O ShutUp but has more Windows configuration options built in. Like a customizer with additional privacy focus.\nJust click “Analyze”, then “Run Fixer” and your settings will be applied.\nCrapFixer user interface\n✅ Free to use and highly recommended!\nRemoveWindowsAI Another tool that made a good impression: RemoveWindowsAI.\nIt’s a collection of PowerShell scripts focused on removing AI features like Copilot, Recall, or AI-editing tools in Paint or Notepad.\nIt even tries to prevent the reinstallation of AI features from Microsoft by replacing the packages with dummy versions. So far, this works great for me!\nUsage is not that easy. You need to be comfortable with executing PowerShell commands. The documentation in the repository is excellent and the development is active.\nUse this tool to get rid of more AI stuff in Windows than the others. However, it only focuses on AI. Nothing else.\nRemoveWindowsAI user interface\n✅ Specialized on removing and disabling AI features\nNot recommended tools 👎 Windows Privacy Dashboard (dead since 2021)\nDebotnet (Windows 10 only, dead since 2019)\nWindows10Debloater (Windows 10 only, dead since 2021)\nDestroyWindows10Spying (Windows 10 only, dead since 2016)\nAlthough the tools are dead, some of the scripts could be working. A lot of experimenting is required here.\nConclusion With this guide, you can get rid of a lot of AI slop in Windows. Say goodbye to Recall and Copilot. The proposed tools are reliable and stable. Just remember to create a backup before making any changes. A bad registry modification could ruin your entire system.\n","permalink":"https://quickcoder.org/posts/disable-ai-in-windows/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eWindows 11 is forcing us to use AI, ads, and many other features most people don’t want. Here is how you disable AI in Windows.\u003c/p\u003e\n\u003cp\u003eIn recent years, Microsoft changed Windows from a local operating system to an always-online, cloud-based, agentic one full of AI slop, ads, and unwanted features.\u003c/p\u003e\n\u003cp\u003eHere are some of the feature that nobody asked for and nobody wants:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eWindows Recall\u003c/li\u003e\n\u003cli\u003eWindows Copilot\u003c/li\u003e\n\u003cli\u003eStart menu searches the web\u003c/li\u003e\n\u003cli\u003eAds everywhere\u003c/li\u003e\n\u003cli\u003eExcessive telemetry information\u003c/li\u003e\n\u003cli\u003e…\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eI have been using Windows for years and it wasn’t a problem until recently. Due to some app constraints, I cannot switch to Linux and I am also not a big fan of virtual machines or dual boot solutions. \u003c/p\u003e","title":"Hate The AI Slop In Windows? Here Is How To Disable AI in Windows"},{"content":"Introduction Google Fonts contains over 1,800 different fonts that are free to use. You’ll find the perfect font for any app in their repository. But how do you get them in your app? The usual way is to download the fonts, add them to your pubspec.yaml, and configure them in your app. But there is an easier way. So here is how to use Google Fonts in your Flutter app!\nInstall the package Manually installing fonts is not hard, but it’s a bit annoying. The google_fonts package takes care of most of it. Install the package for your app.\n# install with CLI flutter pub add google_fonts or\n# add manually to pubspec.yaml name: flutter_test description: \u0026#34;A new Flutter project.\u0026#34; publish_to: \u0026#39;none\u0026#39; version: 1.0.0+1 environment: sdk: ^3.8.1 flutter: 3.35.2 dependencies: flutter: sdk: flutter flutter_web_plugins: sdk: flutter cupertino_icons: ^1.0.8 google_fonts: ^6.3.1 This saves you from having to download and manually add the font files.\nApply the configuration Next, head over to your main.dart (or the file where your MaterialApp instance is created).\nChoose the desired font (for example, prompt) and assign the corresponding TextTheme object in your ThemeData constructor. This configures all properties in the TextTheme class to use that font. No matter if you want bodySmall or labelLarge, the font will always be the same!\nclass MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( ... theme: ThemeData( textTheme: GoogleFonts.promptTextTheme()) ); } And that is it!\nAll it takes is 1 minute of work. Compare this with the manual process!\nConclusion In this article, you learned how to use Google Fonts in your Flutter app. While you can do everything manually, the google_fonts package offers a simple and fast solution to get the job done. I’d recommend using it whenever possible!\n","permalink":"https://quickcoder.org/posts/how-to-use-google-fonts-in-your-flutter-app/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003e\u003ca href=\"https://fonts.google.com/\"\u003eGoogle Fonts\u003c/a\u003e contains over 1,800 different fonts that are free to use. You’ll find the perfect font for any app in their repository. But how do you get them in your app? The \u003ca href=\"https://docs.flutter.dev/cookbook/design/fonts\"\u003eusual way\u003c/a\u003e is to download the fonts, add them to your \u003ccode\u003epubspec.yaml\u003c/code\u003e, and configure them in your app. But there is an easier way. So here is how to use Google Fonts in your Flutter app!\u003c/p\u003e\n\u003ch1 id=\"install-thepackage\"\u003eInstall the package\u003c/h1\u003e\n\u003cp\u003eManually installing fonts is not hard, but it’s a bit annoying. The \u003ca href=\"https://pub.dev/packages/google_fonts\"\u003egoogle_fonts\u003c/a\u003e package takes care of most of it. Install the package for your app.\u003c/p\u003e","title":"How To Use Google Fonts In Your Flutter App"},{"content":"Introduction I tried to do something pretty simple in my opinion. Have a widget handle tap, long press, double tap, and right click events. Making it work is rather easy, but it’s way harder to test than I ever imagined. Here is my approach on how to handle tap gestures in widget tests.\nTesting tap events I started with a ListTile which offers onTap and onLongPress to begin with. Things went smoothly and here is a working widget test:\ntestWidgets(\u0026#34;Test\u0026#34;, (tester) async { bool tapped = false; bool longPressed = false; final widgetToTest = MaterialApp( home: Scaffold( body: SingleChildScrollView( child: Column( children: [ ListTile( onTap: () { tapped = true; }, onLongPress: () { longPressed = true; }, title: SizedBox( width: 200, height: 100, child: Text(\u0026#34;my list tile\u0026#34;), ), ), ], ), ), ), ); await tester.pumpWidget(widgetToTest); await tester.pumpAndSettle(); await tester.tap(find.byType(Text)); await tester.pump(); expect(tapped, true, reason: \u0026#34;NOT TAPPED\u0026#34;); await tester.longPress(find.byType(Text)); await tester.pump(); expect(longPressed, true, reason: \u0026#34;NOT LONG PRESSED\u0026#34;); }); But then I wanted to have right click and double click events for desktop app support. ListTile doesn’t offer this behavior so I wrapped it in a GestureDetector which offers everything I wanted. Here is the updated example code:\ntestWidgets(\u0026#34;Test\u0026#34;, (tester) async { bool tapped = false; bool longPressed = false; bool doubleTapped = false; final widgetToTest = MaterialApp( home: Scaffold( body: SingleChildScrollView( child: Column( children: [ GestureDetector( onDoubleTap: () { doubleTapped = true; }, child: ListTile( onTap: () { tapped = true; }, onLongPress: () { longPressed = true; }, title: SizedBox( width: 200, height: 100, child: Text(\u0026#34;my list tile\u0026#34;), ), ), ), ], ), ), ), ); await tester.pumpWidget(widgetToTest); await tester.pumpAndSettle(); await tester.tap(find.byType(Text)); await tester.pump(); expect(tapped, true, reason: \u0026#34;NOT TAPPED\u0026#34;); await tester.longPress(find.byType(Text)); await tester.pump(); expect(longPressed, true, reason: \u0026#34;NOT LONG PRESSED\u0026#34;); await tester.tap(find.byType(Text)); await tester.tap(find.byType(Text)); await tester.pump(); expect(doubleTapped, true, reason: \u0026#34;NOT DOUBLE TAPPED\u0026#34;); }); Should work, but doesn’t.\nThe onTap event in the widget test did not work as expected\nIt’s interesting that the tap is failing. So it seems that ListTile and GestureDetector don’t work well together. Sure, there must be a way to test this. So I started digging around.\nList of not working approaches I am a bit disappointed at this point because I couldn’t get it to work. And I tried a lot. Here are some approaches that I tried.\nMove ListTile events to GestureDetector So instead of\nGestureDetector( onDoubleTap: () { doubleTapped = true; }, child: ListTile( onTap: () { tapped = true; }, onLongPress: () { longPressed = true; }, title: SizedBox( width: 200, height: 100, child: Text(\u0026#34;my list tile\u0026#34;), ), ), ), I tried\nGestureDetector( onDoubleTap: () { doubleTapped = true; }, onTap: () { tapped = true; }, onLongPress: () { longPressed = true; }, child: ListTile( title: SizedBox( width: 200, height: 100, child: Text(\u0026#34;my list tile\u0026#34;), ), ), ), Result was the same. All taps weren’t recognized as expected.\nReplace ListTile with a Container Maybe I could get around the problem by replacing the ListTile with something that does not handle any tap gestures. I tried various widgets but sadly, no improvements in the results.\nIn addition, I was really surprised that I couldn’t find any bug reports or articles about that topic across the internet. Is nobody writing widget tests or just not sharing their experiences?\nI came to the conclusion that GestureDetector is not the way to go. Moving on to…\nInkWell to the rescue! Oh wait… InkWell is essentially a GestureDetector with effects in simple words.\nBtw how cool is that widget name? A fountain of paint that has splash effects. Naming done right by the Flutter team! ❤️\nI introduced InkWell … and nothing changed. NOTHING. The same errors, the same problems. With or without the ListTile.\nTime to fight with the big guns. I let Claude Sonnet 4 loose. This is always my last resort because it’s better than GPT 4.1 but my requests to Sonnet are limited in my GitHub Copilot license.\nAI tries its luck Claude Sonnet 4 gave everything but after 40 minutes of mostly trying what I had already tried (yes, I explained in the prompt which approaches I had already tried), I told it to revert all changes to its original state.\n💡 Tip\nAlways commit before giving control to an AI agent. Claude couldn’t restore the initial state. I had to do it manually.\nI was about to give up but with a last search I found a working solution!\nThe hacky but working solution My greetings go out to StackOverflow. I hope it never dies because it’s a goldmine for strange problems and solutions.\nThe idea is to not use the widget tester to tap the InkWell or GestureDetector but invoke the methods on the widgets.\nI had no idea that this was even possible. Here is the working test code:\ntestWidgets(\u0026#34;Test\u0026#34;, (tester) async { bool tapped = false; bool longPressed = false; bool doubleTapped = false; ValueKey key = ValueKey(\u0026#34;a\u0026#34;); final widgetToTest = MaterialApp( home: Scaffold( body: SingleChildScrollView( child: Column( children: [ GestureDetector( key: key, onDoubleTap: () { doubleTapped = true; }, onTap: () { tapped = true; }, onLongPress: () { longPressed = true; }, child: ListTile( title: SizedBox( width: 200, height: 100, child: Text(\u0026#34;my list tile\u0026#34;), ), ), ), ], ), ), ), ); await tester.pumpWidget(widgetToTest); await tester.pumpAndSettle(); final gd = find.byKey(key).evaluate().first.widget as GestureDetector; gd.onTap!(); await tester.pump(); expect(tapped, true, reason: \u0026#34;NOT TAPPED\u0026#34;); gd.onLongPress!(); await tester.pump(); expect(longPressed, true, reason: \u0026#34;NOT LONG PRESSED\u0026#34;); gd.onDoubleTap!(); await tester.pump(); expect(doubleTapped, true, reason: \u0026#34;NOT DOUBLE TAPPED\u0026#34;); }); Your InkWell/GestureDetector needs a key to identify it. The type doesn’t matter, it works with Key, ValueKey, UniqueKey, or GlobalKey.\nThen grab the widget and cast it to the corresponding type. The last step is to call the desired methods directly. Not pretty or intuitive but after hours of trying, I am pragmatic in that regard.\nI hope this article saves someone the pain and frustration that I encountered!\nConclusion In this article, I talked about how to handle tap gestures in widget tests. There is always a solution for every problem in the Flutter world, but this time it is not pretty. When you have issues with InkWell or GestureDetector, then this article should help you.\n","permalink":"https://quickcoder.org/posts/how-to-handle-tap-gestures-in-widget-tests/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eI tried to do something pretty simple in my opinion. Have a widget handle tap, long press, double tap, and right click events. Making it work is rather easy, but it’s way harder to test than I ever imagined. Here is my approach on how to handle tap gestures in widget tests.\u003c/p\u003e\n\u003ch1 id=\"testing-tapevents\"\u003eTesting tap events\u003c/h1\u003e\n\u003cp\u003eI started with a \u003ccode\u003eListTile\u003c/code\u003e which offers \u003ca href=\"https://api.flutter.dev/flutter/material/ListTile/onTap.html\"\u003eonTap\u003c/a\u003e and \u003ca href=\"https://api.flutter.dev/flutter/material/ListTile/onLongPress.html\"\u003eonLongPress\u003c/a\u003e to begin with. Things went smoothly and here is a working widget test:\u003c/p\u003e","title":"How To Handle Tap Gestures In Widget Tests"},{"content":"Introduction Flutter web apps become more and more popular and the Flutter team has made significant improvements over the last years to the platform.\nA good web app needs to be responsive these days. Flutter offers some solutions here (LayoutBuilder, MediaQuery), but testing still feels hard.\nI came up with some solutions myself which I want to share. So here is how to test your responsive Flutter app.\nBreakpoints This article focuses on widget testing since this is the layer that handles the responsiveness. The basic idea is to run every test with a different resolution with minimal effort. I use this little helper in various forms:\nclass TestScreenSizes { static final mobile = TestScreenSize( \u0026#39;Mobile\u0026#39;, Size(UiBreakpoints.mobileWindowWidth, UiBreakpoints.mobileWindowHeight), ); static final tablet = TestScreenSize( \u0026#39;Tablet\u0026#39;, Size(UiBreakpoints.tabletWindowWidth, UiBreakpoints.tabletWindowHeight), ); static final desktop = TestScreenSize( \u0026#39;Desktop\u0026#39;, Size(UiBreakpoints.desktopWindowWidth, UiBreakpoints.desktopWindowHeight), ); static final fullHd = TestScreenSize( \u0026#39;FullHD\u0026#39;, Size(UiBreakpoints.fullHdWindowWidth, UiBreakpoints.fullHdWindowHeight), ); static final all = [mobile, tablet, desktop, fullHd]; } class TestHelper { static Future\u0026lt;void\u0026gt; setScreenSize( WidgetTester tester, TestScreenSize screenSize, ) async { await tester.binding.setSurfaceSize(screenSize.size); await tester.pumpAndSettle(); } } class TestScreenSize { final String name; final Size size; const TestScreenSize(this.name, this.size); } class UiBreakpoints { static double fullHdWindowWidth = 1920; static double fullHdWindowHeight = 1080; static double desktopWindowWidth = 1200; static double desktopWindowHeight = 800; static double tabletWindowWidth = 768; static double tabletWindowHeight = 1024; static double mobileWindowWidth = 375; static double mobileWindowHeight = 667; } I defined four screen sizes with common dimensions for mobile, tablet, desktop, and full HD resolutions. Depending on how many breakpoints you have in your app, not all might be needed.\nLayoutBuilder is usually my widget of choice to distinguish between those breakpoints. Testing As mentioned earlier, I run all tests in every resolution. Here is a snippet of how it works:\nvoid main() { group(\u0026#34;Product List\u0026#34;, () { for (final size in TestScreenSizes.all) { testWidgets(\u0026#34;Test default elements (${size.name})\u0026#34;, (tester) async { await TestHelper.setScreenSize(tester, size); await tester.pumpWidget(SomeWidget()); await tester.pumpAndSettle(); expect(find.byType(GroupHeaderWidget), findsOneWidget); expect(find.byType(ProductTableHeaderWidget), findsOneWidget); expect(find.byType(ProductTableWidget), findsOneWidget); expect(find.byType(FilterOptionsWidget), findsOneWidget); expect(find.byType(ExportDataWidget), findsOneWidget); expect(find.byType(JumpToStartWidget), findsOneWidget); expect(find.byType(GroupHeaderWidget), findsOneWidget); expect(find.byType(Badge), findsNothing); }); } }); } There is a for loop that iterates over all sizes and inside the test, I set the dimensions at the beginning with the setSurfaceSize method. The test name includes the screen size so that I can identify failing ones better if they only fail with certain resolutions.\nBasically, I am wrapping all my tests in a loop and set the size at the start. This already covers the majority of issues. But now, let’s move on to fine tuning.\nDrawbacks Widget tests aren’t perfect. I had many problems with them in the past (#1, #2). But during responsive testing, you enter a different hell: RenderFlex Overflow errors.\nIt can happen that you get these in your tests but they don’t appear in your running app. Fixing them is usually the best solution but in some cases, it’s not possible or not worth the effort. Then, your only solution is to ignore them and consider them as false positives. Here is how (thanks to Eduardo Yamauchi on Stackoverflow):\nFlutterError.onError = _onError_ignoreOverflowErrors; Function _onError_ignoreOverflowErrors = ( FlutterErrorDetails details, { bool forceReport = false, }) { assert(details != null); assert(details.exception != null); // --- bool ifIsOverflowError = false; // Detect overflow error. var exception = details.exception; if (exception is FlutterError) ifIsOverflowError = !exception.diagnostics .any((e) =\u0026gt; e.value.toString().startsWith(\u0026#34;A RenderFlex overflowed by\u0026#34;)); // Ignore if is overflow error. if (ifIsOverflowError) print(\u0026#39;Overflow error.\u0026#39;); // Throw others errors. else FlutterError.dumpErrorToConsole(details, forceReport: forceReport); }; The downside is that you cannot set this globally or in a setUp method. You need to call it in every test. And it will also cover real overflow errors. So maybe it’s a good thing that you cannot ignore them globally.\nAnother downside of my approach is that when you have different widgets for mobile and desktop, expecting gets messy. You end up with many comparisons like these:\nexpect(find.byType(SomeWidget), size == TestScreenSizes.mobile ? findsNothing : findsOneWidget); When you first start introducing responsive testing, you’ll spent a lot of time fixing those errors. On the other hand, you can be sure that your responsive design is tested to some extent at least.\nIt depends on you and your app if these kinds of tests are worth it or not.\nConclusion Responsive testing is messy in Flutter. In this article, I showed you my approach of how to test your responsive Flutter app. It’s not perfect, but it gets the job done. For complex apps, I would recommend a responsive testing strategy any time.\n","permalink":"https://quickcoder.org/posts/how-to-test-your-responsive-flutter-app/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eFlutter web apps become more and more popular and the Flutter team has made significant improvements over the last years to the platform.\u003c/p\u003e\n\u003cp\u003eA good web app needs to be responsive these days. Flutter offers some solutions here (\u003ca href=\"https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html\"\u003eLayoutBuilder\u003c/a\u003e, \u003ca href=\"https://api.flutter.dev/flutter/widgets/MediaQuery-class.html\"\u003eMediaQuery\u003c/a\u003e), but testing still feels hard.\u003c/p\u003e\n\u003cp\u003eI came up with some solutions myself which I want to share. So here is how to test your responsive Flutter app.\u003c/p\u003e\n\u003ch1 id=\"breakpoints\"\u003eBreakpoints\u003c/h1\u003e\n\u003cp\u003eThis article focuses on widget testing since this is the layer that handles the responsiveness. The basic idea is to run every test with a different resolution with minimal effort. I use this little helper in various forms:\u003c/p\u003e","title":"How To Test Your Responsive Flutter App"},{"content":"Introduction When consuming large REST APIs, it requires writing a lot of boilerplate code to support all required endpoints. To make your life easier, you can use generators to automatically create type-safe production code for your apps. I’ll show you how to do this in this article. All it takes are 5 minutes of setup and you can enjoy the best way to consume APIs in your apps.\nRequirements This approach only works if the API source offers an open API specification file to generate the code. It can be either in json or yaml format and is usually available for download in the documentation of the API.\nGenerators Open API generators are available for many programming languages. In this example, we’ll focus on Dart code. You need the packages openapi_generator and openapi_generator_annotations. dependencies: openapi_generator_annotations: ^latest dev_dependencies: openapi_generator: ^latest Under the hood, these tools use the openapi-generator base library written in Java. If you develop for Android, chances are high that it is already installed on your system. Otherwise, you have to install it.\nGenerating API packages The generator parses the specification file and creates a package to consume the api. You can store this in your project, on GitHub, or even on pub.dev or your own package repository to reuse.\nInstructions for the generator are supplied via annotations. Here is an example:\nimport \u0026#39;package:openapi_generator_annotations/openapi_generator_annotations.dart\u0026#39;; @Openapi( additionalProperties: AdditionalProperties(pubName: \u0026#34;park_api\u0026#34;, ), inputSpec: InputSpec(path: \u0026#34;lib/api/specs/document.json\u0026#34;), generatorName: Generator.dart, outputDirectory: \u0026#39;packages/park_api\u0026#39;, ) class ParkApiExample {} This is a minimal form but there are many more options to configure the generator.\npubName is the name of the generated package The InputSpec path tells the generator where it can find the specification file. There is also a RemoteSpec object which accepts a URL path. generatorName defines what underlying library is used for HTTP requests. In Dart, you change choose between http (Generator.dart) and dio (Generator.dio). With the outputDirectory you can specify where to put the generated files. You can put this file anywhere and also include multiple specifications in it.\nTo create the package, run the build runner:\ndart run build_runner build --delete-conflicting-outputs After some seconds, new packages should appear in your project folder.\nGenerated packages of the openapi/_generator in a Flutter app\nHave a look at the generated model classes for one of my APIs. It’s a massive list and it would take you a while to type that stuff 😉\nList of created model classes of a generated API package\nThe next step is to find out how we can use the package in our app.\nUsing generated packages To use the package, you need to import it in your pubspec.yaml first.\nExample of how to import the created packages in a Flutter app\nIn my case, I created a bunch of packages for different APIs and included them all.\nTo access the endpoints, you need to create an instance of DefaultApi which contains all endpoints as methods. You can then work with the API in a type-safe and object-oriented way rather than composing requests and parsing responses manually.\nIntellisense auto-completion shows all available endpoints for a given API in VSCode\nThis approach saves you time, you’ll have fewer bugs, and the development feels easier overall. Code generators are a life-saving tool in that case as you have seen.\nDealing with code errors The Open API Specification is quite complex to support many use cases. It is possible that the generator will create invalid code in some cases. Sometimes, it is because the API spec has a problem and sometimes, the generator doesn’t support this case yet.\nExample of a code generation error of the openapi_generator package for an API\nIf you encounter a problem, you have a few options here:\nFix the generated code manually. This is probably the fastest approach but you have to do it every time when you rerun the generator! Ask the API maintainer to investigate. It can happen that a declaration is missing in the code which leads to the invalid generation. Open a bug in the OpenAPI Tools GitHub repo or look for possible workarounds. Filing a bug is the most time-consuming approach. Conclusion For large APIs, generating the code from a specification is the best way APIs in your apps. Don’t waste time writing requests and parsing responses manually. Go the type-safe way and skip the part of writing boilerplate code. Let code generators do the heavy lifting for you!\n","permalink":"https://quickcoder.org/posts/the-best-way-to-consume-apis-in-your-apps/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eWhen consuming large REST APIs, it requires writing a lot of boilerplate code to support all required endpoints. To make your life easier, you can use generators to automatically create type-safe production code for your apps. I’ll show you how to do this in this article. All it takes are 5 minutes of setup and you can enjoy the best way to consume APIs in your apps.\u003c/p\u003e\n\u003ch1 id=\"requirements\"\u003eRequirements\u003c/h1\u003e\n\u003cp\u003eThis approach only works if the API source offers an \u003ca href=\"https://swagger.io/specification/\"\u003eopen API specification\u003c/a\u003e file to generate the code. It can be either in json or yaml format and is usually available for download in the documentation of the API.\u003c/p\u003e","title":"The Best Way To Consume APIs In Your Apps!"},{"content":"Introduction I have read more and more articles in recent months saying that Firebase is too expensive. While that might be true for some cases, it is not for others. In this article, I want to present 4 Firebase services that are always free.\nNo credit card information, no bank accounts required, no usage limits. All you need is a Firebase account and a Firebase project to get started.\nFirebase Remote Config 📺 YouTube Tutorial\nWant to change your app without pushing a new version?\nFirebase Remote Config can do that for you. Simply put, you define placeholders in your app code, call the Remote Config server, and replace the placeholders with the actual values.\nCommon use cases are percentage rollout mechanisms for features, promotion banners, or optimizing ad impression revenue. All this is possible with a simple parameter configuration in the web UI.\nQuick links for you:\nParameters and Conditions\nRecommended loading strategies\nCombine with Firebase Analytics\nFirebase Crashlytics 📺 YouTube Tutorial\nYour users complain about frequent app crashes?\nSounds like Firebase Crashlytics could help you with that. It’s a crash reporting service for mobile apps that collects information and logs, categorizes them, and groups them together in a clean UI.\nYou’ll know exactly what happened right before the crash and how many of your users are affected. You will be back in 5 star review regions in no time when using this tool!\nFirebase Analytics 📺 YouTube Tutorial\nFirebase Analytics is the gold standard when it comes to measure anything in your app for free. Select from hundreds of distinct events or define your own to track exactly what you need.\nLearn what features are useful, which screens are avoided, where users cancel the purchasing process, what countries are important, and which age groups your users are part of.\nYou can get the same power of Google Analytics for websites in your mobile apps!\nMore information:\nRecommended events by Google\nTesting and debugging\nLogging events\nFirebase App Distribution 📺 Youtube Tutorial\nWith Firebase App Distribution, you don’t have to deal with Google Play Console or Apple TestFlight anymore to distribute app versions to your testers.\nThe service unifies both platforms in a single user interface. Manage and collect feedback from all your testers and app versions in one place, integrate it in your CI/CD pipelines, and automate the entire process with the Firebase CLI or fastlane.\nOnce you’ve set it up, it will save you many hourse of manual labor!\nHere are some links to get you started:\nManage test users\nCollect feedback from testers\nAutomate app distribution (Android, iOS)\nConclusion Yes, Firebase can cost some serious money if done wrong. But to say that Firebase is always costly is just a myth. In this article, you learned about 4 Firebase services that are always free. They are the backbone of many enterprise and private projects and have proven their stability and usefulness over years.\nGo check them out. They will make your life easier!\n","permalink":"https://quickcoder.org/posts/4-firebase-services-that-are-always-free/images/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eI have read more and more articles in recent months saying that Firebase is too expensive. While that might be true for some cases, it is not for others. In this article, I want to present 4 Firebase services that are always free.\u003c/p\u003e\n\u003cp\u003eNo credit card information, no bank accounts required, no usage limits. All you need is a \u003ca href=\"/posts/firebase-flutter-setup/\"\u003eFirebase account and a Firebase project\u003c/a\u003e to get started.\u003c/p\u003e\n\u003ch1 id=\"firebase-remoteconfig\"\u003eFirebase Remote Config\u003c/h1\u003e\n\u003cp\u003e📺 \u003ca href=\"https://www.youtube.com/watch?v=%5c_CXXVFPO6f0\"\u003eYouTube Tutorial\u003c/a\u003e\u003c/p\u003e","title":"4 Firebase Services That Are Always Free"},{"content":"Introduction Sick of Material Design? Try out the best Flutter UI library alternatives to make your app stand out from the rest!\nMaterial and Cupertino designs have been part of the Flutter ecosystem since the beginning. But sometimes you want your app to look differently without doing all the styling. In this article, I am presenting the best Flutter UI library alternatives found on pub.dev.\nAll presented packages offer a large amount of widgets ready to be used in your apps. The goal is to spend less time with styling and more time with feature development.\nfluent_ui (31+ components) Fluent UI is the standard design language of Microsoft and used by many apps from their ecosystem like Word, Teams, PowerPoint, or OneDrive. You can choose The package is well maintained and the documentation is great with component previews and code examples.\nIf you want to design a Windows desktop app with Flutter, then this should be your first choice to make the app look native.\nFluent UI widgets for Flutter apps\nshadcn_ui (34+ components) UI library based on the shadcn design system. Minimalistic, accessible components with a clean look and feel. The system is also available for many other coding environments and frameworks.\nThis package is also under active development and updated regularly with new components or adaptations to the latest Flutter versions. The documentation is exemplary.\nShadcn UI widgets for Flutter apps\ngetwidget (32+ components) Getwidget is a Flutter development company and they open sourced their UI library under the same name.\nTheir approach is more colorful by default and the documentation helps you navigate through the enormous amount of widgets with images and code examples. The system is also open to contributions.\nGetwidget widgets for Flutter apps\nforui (46+ components) Forui is a Flutter UI library that focuses on a beautiful design and minimalistic widgets according to the package description.\nA well structured and complete documentation together with CLI support to showcase many examples make this package a viable alternative to the standard Material design.\nThey even have a public roadmap in their GitHub repository!\nForui widgets for Flutter apps\nnes_ui (25+ components) Ok, this might sound like a joke but it’s real: A UI library in the style of the old Nintendo Entertainment System from the 1980s!\nWant to revive good old times? Here is your chance!\nThe pixelated widgets can all be found in the documentation and they work. It’s an interesting alternative for games in my opinion.\nNES UI widgets for Flutter apps\nhux (18+ components) Hux is a rather new library that focuses on clean, minimal, and consistent user interfaces. It has everything you need to create a solid design foundation in your app.\nBuilt-in theming support, automatic contrast setting for vision impaired users, screen reader support out of the box, and a proper documentation make this library a viable choice.\nIf you miss something, file an issue. Chances are high that the team will work on it!\nHux UI widgets for your Flutter apps\nPlatform support overview Android iOS Web Windows MacOS Linux fluent_ui ✅ ✅ ✅ ✅ ✅ ✅ shadcn_ui ✅ ✅ ✅ ✅ ✅ ✅ getwidget ✅ ✅ ✅ ✅ ✅ ✅ forui ✅ ✅ ✅ ✅ ✅ ✅ nes_ui ✅ ✅ ❌ ✅ ✅ ✅ hux ✅ ✅ ✅ ✅ ✅ ✅ Platform support per UI library according to the package description\nConclusion In this article, we’ve learned about the best Flutter UI library alternatives currently around. They help you style your apps quickly by offering predefined widgets ready to use. If you are sick of the standard Material or Cupertino design, then give them a try!\n","permalink":"https://quickcoder.org/posts/best-flutter-ui-library-alternatives/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eSick of Material Design? Try out the best Flutter UI library alternatives to make your app stand out from the rest!\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://docs.flutter.dev/ui/widgets/material\"\u003eMaterial\u003c/a\u003e and \u003ca href=\"https://docs.flutter.dev/ui/widgets/cupertino\"\u003eCupertino\u003c/a\u003e designs have been part of the Flutter ecosystem since the beginning. But sometimes you want your app to look differently without doing all the styling. In this article, I am presenting the best Flutter UI library alternatives found on \u003ca href=\"https://pub.dev\"\u003epub.dev\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eAll presented packages offer a large amount of widgets ready to be used in your apps. The goal is to spend less time with styling and more time with feature development.\u003c/p\u003e","title":"The Best Flutter UI Library Alternatives"},{"content":"Introduction I am exploring Firebase Studio to find out if it’s already a serious competitor for Windsurf or Cursor.\nFirebase Studio is the new kid on the block after Cursor and Windsurf made vibe coding generally available. Instead of writing code directly, users prompt the AI to get a working app for mobiles or the web. In this article, I am exploring Firebase Studio and find out if the tool is already worth a try or not.\nThe product is currently only available in a preview version with limited features. Let’s see how the current state is and what it’s going to be like in the future.\nCreating apps In Firebase Studio, you have two options: Prototyping view and code view.\nThe prototyping view is a chat interface with a live preview of the result. You can immediately see the results of your input.\n❗ Warning\nThis prototyper is limited to web apps with next.js! Other frameworks are currently not supported!\nFirebase Studio Prototype View\nThe code view looks like a Visual Studio Code editor in a browser. Project structure on the left, code in the center, preview and/or chat window on the right.\nIn this view, you have more options and Firebase Studio already proposes many frameworks like Flutter, Angular, and more when creating a new project.\nFirebase Studio Code View\nPain points Like with all current AIs, errors happen. But at least, AI can deal with those errors most of the time. It’s just some extra prompting rounds to get it done.\nIn terms of model selection, there are only the Google models to choose from. Firebase Studio LLM Selection Options\nAnd the biggest scandal for me is that Firebase Studio cannot interact with Firebase at all. Creating a project? Your job. Activating the email/password provider in Firebase Authentication? Manual labor. Setting security rules for a Cloud Firestore database? No support. Maybe this will be added in the future but it thought that this was the whole point of Firebase Studio. Vibe coding and not even dealing with SaaS stuff in the background.\nMakes the tool pretty much worthless for me.\nWhen you eventually decide to publish, Firebase Studio creates a new project for you even if you connected your app to another one manually before. There is no option to change the project.\nIt’s good to know such stuff to not run into typical first usage errors…\nAdditional features Firebase Studio offers to publish your app to Firebase App Hosting. This works fine and is easy. Just remember to publish first and add Firebase services like Storage, Functions, or Firestore afterwards.\nFirebase Studio App Publishing Workflow\nYou can push your code to GitHub from the code view by publishing your branch. All previous chat interactions represent one commit, so you can easily roll back if you need to. Firebase Studio GitHub Connection\nSadly, the prototyper cannot execute any git commands like switching branches or cherry-picking commits. Once again, you have to do it on your own in the code view.\nPricing Firebase Studio is free for up to 3 workspaces per user. For many Firebase integrations however (like App Hosting, Cloud Storage, or Cloud Functions), you need to activate the Blaze plan which charges you for resource consumption rather than a fixed monthly price.\nAll of this can change any time since Firebase Studio is still in a preview phase!\nLearn more about pricing here!\nConclusion In this article, I was exploring Firebase Studio. It’s a new AI Cloud code editor for vibe coding from Google. Currently, I would not recommend using it. It’s still in a preview state It cannot interact with Firebase services like Firestore or Functions Prototyping only works with next.js Maybe they’ll improve it in the future. But at the moment, I’d recommend GitHub Copilot and VS Code, Windsurf, or Cursor for vibe coding.\n","permalink":"https://quickcoder.org/posts/exploring-firebase-studio/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eI am exploring Firebase Studio to find out if it’s already a serious competitor for Windsurf or Cursor.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://studio.firebase.com\"\u003eFirebase Studio\u003c/a\u003e is the new kid on the block after \u003ca href=\"https://www.cursor.com/\"\u003eCursor\u003c/a\u003e and \u003ca href=\"https://windsurf.com/\"\u003eWindsurf\u003c/a\u003e made vibe coding generally available. Instead of writing code directly, users prompt the AI to get a working app for mobiles or the web. In this article, I am exploring Firebase Studio and find out if the tool is already worth a try or not.\u003c/p\u003e","title":"Exploring Firebase Studio — A New AI Cloud Code Editor"},{"content":"Introduction Mobile apps already benefit from it. Next in line is Flutter web. So here is how to enable hot reload for Flutter web apps!\nDebugging web apps has always been a little painful for me. Imagine you have multiple menus and nested screens. Any change resulted in a hot restart and you had to navigate to the previous screen again to see if the changes were applied. But things are about to get better!\nRecently, the Flutter team added hot reload capabilities for the web platform. In Flutter 3.32, it is not enabled by default. You have to manually set a flag when running the app.\nRun the app from the command line:\nflutter run -d chrome --web-experimental-hot-reload Or adjust your launch.json config file in VS Code by changing the args value:\n{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 \u0026#34;version\u0026#34;: \u0026#34;0.2.0\u0026#34;, \u0026#34;configurations\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;xxx\u0026#34;, \u0026#34;request\u0026#34;: \u0026#34;launch\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;dart\u0026#34;, \u0026#34;args\u0026#34;: [\u0026#34;--web-experimental-hot-reload\u0026#34;] }, { \u0026#34;name\u0026#34;: \u0026#34;xxx (profile mode)\u0026#34;, \u0026#34;request\u0026#34;: \u0026#34;launch\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;dart\u0026#34;, \u0026#34;flutterMode\u0026#34;: \u0026#34;profile\u0026#34;, \u0026#34;args\u0026#34;: [\u0026#34;--web-experimental-hot-reload\u0026#34;] }, { \u0026#34;name\u0026#34;: \u0026#34;xxx (release mode)\u0026#34;, \u0026#34;request\u0026#34;: \u0026#34;launch\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;dart\u0026#34;, \u0026#34;flutterMode\u0026#34;: \u0026#34;release\u0026#34;, \u0026#34;args\u0026#34;: [\u0026#34;--web-experimental-hot-reload\u0026#34;] } ] } Changes are visible immediately and the need for a hot restart is gone for 99% of use cases!\nDrawback I found one small little problem with this feature. I usually develop on Windows and Microsoft Edge is my main debugging browser. However, hot reload does not work in this case. The debugging bar doesn’t show the hot reload symbol (yellow flash).\nWhen running on the Microsoft Edge browser, the hot reload symbol is missing.\nWith Google Chrome, everything works as promised. The button is available and the feature behaves as you would expect it to do.\nWhen running on the Google Chrome browser, the hot reload symbol (yellow flash) is available and working.\nConclusion In this article, I showed you how to enable hot reload in Flutter web apps. It makes your development experience more seamless.\n","permalink":"https://quickcoder.org/posts/how-to-enable-hot-reload-for-flutter-web/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eMobile apps already benefit from it. Next in line is Flutter web. So here is how to enable hot reload for Flutter web apps!\u003c/p\u003e\n\u003cp\u003eDebugging web apps has always been a little painful for me. Imagine you have multiple menus and nested screens. Any change resulted in a hot restart and you had to navigate to the previous screen again to see if the changes were applied. But things are about to get better!\u003c/p\u003e","title":"How To Enable Hot Reload For Flutter Web"},{"content":"Introduction Displaying a popup is a rather trivial task for any seasoned Flutter developer. But when you are using Riverpod as state management solution, it can be a bit tricky. Here is my approach on how to show a popup in your Flutter app with Riverpod.\nWhy addPostFrameCallback doesn’t work First, you might come up with this approach:\nclass MyView extends StatefulWidget { const MyView({super.key}); @override State\u0026lt;MyView\u0026gt; createState() =\u0026gt; _MyViewState(); } class _MyViewState extends State\u0026lt;MyView\u0026gt; { @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) async { await showDialog( context: context, builder: (context){ return SomeDialogWidget(); }); }); } @override Widget build(BuildContext context) { return Container(); } } Using the addPostFrameCallback function is a good way to perform actions after the first frame is rendered. The widget is build and the dialog appears on top of it. Great in theory, but this doesn’t work with Riverpod notifiers.\nHere is the same code but this time, the build method relies on data from a Notifier.\nclass MyView extends ConsumerStatefulWidget { const MyView({super.key}); @override ConsumerState\u0026lt;MyView\u0026gt; createState() =\u0026gt; _MyViewState(); } class _MyViewState extends ConsumerState\u0026lt;MyView\u0026gt; { @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) async { await showDialog( context: context, builder: (context) { return SomeDialogWidget(); }, ); }); } @override Widget build(BuildContext context) { final dataNotifier = ref.watch(someNotifier); return dataNotifier.when( data: (data) =\u0026gt; Container(), loading: () =\u0026gt; CircularProgressIndicator(), error: (e, st) =\u0026gt; ErrorWidget(e), ); } } The first build cycle will usually return the loading state. After the rendering, the addPostFrameCallback function is executed and the dialog is shown. However, as soon as the notifier changes its state, another rebuild is triggered (with either data or error state) and the dialog is gone. You might not even have seen it although it was there for a short amount of time!\nIt doesn’t matter if you have a stateless or stateful widget. Riverpod notifiers always start in the loading state before returning data or an error which leads to one inital build and one rebuild. The rebuild wipes your dialog.\nMoving the addPostFrameCallback to the build method could work in some cases. However, for one-time popups this isn’t an option. The dialog would reappear every time the notifier state changes.\nref.listen is the solution Riverpod offers a solution for this problem: ref.listen(). In short: It works similar to ref.watch() but doesn’t trigger a rebuild. For more details have a look at these articles here:\nFlutter Riverpod 2.0: The Ultimate Guide\nref.read, ref.watch, and ref.listen in Riverpod 2\nAll we need is a little notifier that stores a boolean to indicate if the dialog can be shown. Here is an example:\nfinal canShowDialogNotifier = NotifierProvider\u0026lt;DialogNotifierState, bool\u0026gt;( DialogNotifierState.new, ); class DialogNotifierState extends Notifier\u0026lt;bool\u0026gt; { @override bool build() { ref.keepAlive(); return false; } void setState(bool newState){ state = newState; } } You can also reverse the logic and let the notifier indicate if the dialog has already been shown. Just make sure to keep it alive or the dialog might pop up again without you wanting it :)\nNext, we wire everything up in the build method of our widget:\nclass MyView extends ConsumerWidget { const MyView({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { ref.listen\u0026lt;bool\u0026gt;(canShowDialogNotifier, (prev, next) async { if (next) { await showDialog( context: context, builder: (context) { return SomeDialogWidget(); }, ); } }); final dataNotifier = ref.watch(someNotifier); return dataNotifier.when( data: (data) =\u0026gt; Container(), loading: () =\u0026gt; CircularProgressIndicator(), error: (e) =\u0026gt; ErrorWidget(e), ); } } next contains the new notifier value, prev the old one. You can add more logic here to decide if a dialog should be triggered. Change the notifier state from false to true and the dialog will appear.\n💡 Tip\nref.listen requires a state change.\nIt does not react on the value provided by the build method of the notifier!\nYou can even remove the state from the widget (StatefulConsumerWidget -\u0026gt; ConsumerWidget) with this approach. No need for the initState method anymore!\nConclusion In this article you learned how to show a popup in your Flutter app with Riverpod. As we have seen, addPostFrameCallback doesn’t work in every case but with ref.listen, there is an easy alternative to make it work again.\n","permalink":"https://quickcoder.org/posts/how-to-show-a-popup-in-your-flutter-app-with-riverpod/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eDisplaying a popup is a rather trivial task for any seasoned Flutter developer. But when you are using Riverpod as state management solution, it can be a bit tricky. Here is my approach on how to show a popup in your Flutter app with Riverpod.\u003c/p\u003e\n\u003ch1 id=\"why-addpostframecallback-doesntwork\"\u003eWhy addPostFrameCallback doesn’t work\u003c/h1\u003e\n\u003cp\u003eFirst, you might come up with this approach:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-dart\" data-lang=\"dart\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eMyView\u003c/span\u003e \u003cspan class=\"kd\"\u003eextends\u003c/span\u003e \u003cspan class=\"n\"\u003eStatefulWidget\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"n\"\u003eMyView\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\u003cspan class=\"k\"\u003esuper\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"err\"\u003e@\u003c/span\u003e\u003cspan class=\"n\"\u003eoverride\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eState\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eMyView\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ecreateState\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003e_MyViewState\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003e_MyViewState\u003c/span\u003e \u003cspan class=\"kd\"\u003eextends\u003c/span\u003e \u003cspan class=\"n\"\u003eState\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eMyView\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"err\"\u003e@\u003c/span\u003e\u003cspan class=\"n\"\u003eoverride\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003einitState\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003esuper\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einitState\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eWidgetsBinding\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einstance\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eaddPostFrameCallback\u003c/span\u003e\u003cspan class=\"p\"\u003e((\u003c/span\u003e\u003cspan class=\"n\"\u003e_\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"kd\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eshowDialog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nl\"\u003econtext:\u003c/span\u003e \u003cspan class=\"n\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nl\"\u003ebuilder:\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eSomeDialogWidget\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"err\"\u003e@\u003c/span\u003e\u003cspan class=\"n\"\u003eoverride\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eWidget\u003c/span\u003e \u003cspan class=\"n\"\u003ebuild\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eBuildContext\u003c/span\u003e \u003cspan class=\"n\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eContainer\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUsing the \u003ccode\u003eaddPostFrameCallback\u003c/code\u003e function is a good way to perform actions after the first frame is rendered. The widget is build and the dialog appears on top of it. Great in theory, but this doesn’t work with Riverpod notifiers.\u003c/p\u003e","title":"How To Show A Popup In Your Flutter App With Riverpod"},{"content":"Introduction Flutter offers several types of tests that you can use to ensure correct app behavior. There are unit tests, integration tests, and finally widget tests. In this article, we’ll have a look at how to do widget testing in Flutter apps. Improve your testing knowledge with this guide. This means that a part of the app is rendered during the tests and you can describe test steps to copy the behavior of a real user. The Flutter SDK offers methods to simulate a complete workflow.\nThe app to test I created a simple app that needs some widget tests. You can see a demo below.\nDemo Flutter application to be tested by author\nThe app contains a Scaffold with a static AppBar, a description Text, a ListView, and a FloatingActionButton. When you click on the button, a new row is inserted into the ListView with a random number between 0 and 10, the header text is updated, and a SnackBar is shown. You can also delete entries with a long press. A simple tap shows a confirmation of what row you clicked. This is what we are going to test now.\nTest scenario As the app workflow is pretty simple, we’ll write just one test that fulfills all requirements. You can, of course, split your tests into multiple ones if it makes sense. Good test organization is essential in larger apps, so take your time and think about what’s reasonable.\nOur test will do the following: (action step, verification step)\nVerify that there are already 3 data items present\nTap the FloatingActionButton\nVerify that a new data item has been added\nTap the 3rd item\nVerify that the SnackBar shows the same data as in the list\nLong press the 2nd item\nVerify that the correct item has been deleted\nThe test skeleton for widget tests is similar to unit tests but has an additional WidgetTester argument. It always looks like this\nDartimport \u0026lsquo;package:flutter_test/flutter_test.dart\u0026rsquo;; void main() { testWidgets(\u0026lsquo;Test description\u0026rsquo;, (WidgetTester tester) async { }); }\nimport \u0026#39;package:flutter_test/flutter_test.dart\u0026#39;; void main() { testWidgets(\u0026#39;Test description\u0026#39;, (WidgetTester tester) async { }); } widgetTester class The WidgetTester is the central unit that performs actions during the test. For example, there are methods to\n▶ write into TextField widgets → enterText\n▶ tap widgets → tap\n▶ trigger animations and rendering → pump, pumpAndSettle\nTo find widgets, you use the Finder class. It has many methods to find exactly what you are looking for. Some examples are\n▶ find.byType\n▶ find.byKey\n▶ find.text\nAuto-completion options of the Finder class in VS Code by author\nFor verification, you use the expect library like in regular unit tests. A typical expect statement can look like this:\nexpect(find.byText(”test”), findsOneWidget);\nLook for one or more specific widgets and assert it.\nFull test code The general workflow in widget tests is always the same. First, you use a Finder to identify a widget you want to interact with. Then, you pass it to the WidgetTester that performs an action and triggers animation and rendering. The final step is to verify the outcome with the Expect class.\nHave a look at the test example below. You can find the entire app source code at the end of the article.\nimport \u0026#39;package:flutter/material.dart\u0026#39;; import \u0026#39;package:flutter_test/flutter_test.dart\u0026#39;; import \u0026#39;package:flutter_widget_test/list_widget.dart\u0026#39;; import \u0026#39;package:flutter_widget_test/main.dart\u0026#39;; void main() { testWidgets(\u0026#39;Test app workflow\u0026#39;, (WidgetTester tester) async { // Build our app and trigger a frame. await tester.pumpWidget(const MyApp()); // Verify that 3 data items exist // Here we look for 3 ListWidget elements which are the widgets that are // used in the ListView. expect(find.byType(ListWidget), findsNWidgets(3)); // Tap the FloatingActionButton // Here we use a Type Finder to find our item. Works great if there is only // one element of a type present. await tester.tap(find.byType(FloatingActionButton)); await tester.pump(); // Verify that new row has been added // We again look for 4 ListWidget elements. Furthermore, we look for a // SnackBar and the matching text that is displayed when a new data item is // added. expect(find.byType(ListWidget), findsNWidgets(4)); expect(find.byType(SnackBar), findsOneWidget); expect(find.text(\u0026#34;item 4 added\u0026#34;), findsOneWidget); // Tap 3rd item in the list // We use a Key Finder to identify the ListWidget we want to tap. Keys are // always unique and a good way to find the exact widget. The pumpAndSettle // is required because the previous SnackBar auto-dismisses after 1 second // and we need to wait for that to happen. await tester.tap(find.byKey(const Key(\u0026#34;ListWidget2\u0026#34;))); await tester.pumpAndSettle(const Duration(milliseconds: 1250)); // Verify shown data is correct // We look for the SnackBar and a certain text. expect(find.byType(SnackBar), findsOneWidget); expect(find.text(\u0026#34;item 3 pressed\u0026#34;), findsOneWidget); // Long press 2nd item // Again a Key Finder and a pumpAndSettle to wait for the previous SnackBar. await tester.longPress(find.byKey(const Key(\u0026#34;ListWidget1\u0026#34;))); await tester.pumpAndSettle(const Duration(milliseconds: 1250)); // Verify pressed item was deleted // Finding widgets by type, by key, or by text. There are many possibilities. expect(find.byType(ListWidget), findsNWidgets(3)); expect(find.byKey(const Key(\u0026#34;ListWidget0\u0026#34;)), findsOneWidget); expect(find.byKey(const Key(\u0026#34;ListWidget1\u0026#34;)), findsOneWidget); expect(find.byKey(const Key(\u0026#34;ListWidget2\u0026#34;)), findsOneWidget); expect(find.byKey(const Key(\u0026#34;ListWidget3\u0026#34;)), findsNothing); expect(find.byType(SnackBar), findsOneWidget); expect(find.text(\u0026#34;item 2 deleted\u0026#34;), findsOneWidget); }); } Conclusion In this article, we explored how to do widget testing in Flutter apps. They are the core testing principle together with unit and integration tests for any Flutter app.\nSource code You can find the source code on GitHub.\n","permalink":"https://quickcoder.org/posts/flutter-widget-testing/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eFlutter offers several types of tests that you can use to ensure correct app behavior. There are \u003ca href=\"https://docs.flutter.dev/cookbook/testing/unit/introduction\"\u003eunit tests\u003c/a\u003e, \u003ca href=\"https://docs.flutter.dev/testing/integration-tests\"\u003eintegration tests\u003c/a\u003e, and finally \u003ca href=\"https://docs.flutter.dev/cookbook/testing/widget/introduction\"\u003ewidget tests\u003c/a\u003e. In this article, we’ll have a look at how to do widget testing in Flutter apps. Improve your testing knowledge with this guide. This means that a part of the app is rendered during the tests and you can describe test steps to copy the behavior of a real user. The Flutter SDK offers methods to simulate a complete workflow.\u003c/p\u003e","title":"How To Do Widget Testing In Flutter Apps"},{"content":"Introduction Celebrating user achievements is a great way to boost engagement in your Flutter apps. In this article, we’ll discover how to add simple confetti animations in Flutter apps . But since we are lazy developers, we use a package instead of creating the animation on our own. With the code examples and demo videos, you’ll be ready to blast your users away with confetti!\nAnimation A proper confetti animation can be a challenge. Lucky for us, there is a package on pub.dev that helps us here.\nHere are some examples of what you can build with it:\nimport \u0026#39;package:confetti/confetti.dart\u0026#39;; import \u0026#39;package:flutter/widgets.dart\u0026#39;; class ConfettiScreen extends StatefulWidget { const ConfettiScreen({super.key}); @override State\u0026lt;ConfettiScreen\u0026gt; createState() =\u0026gt; _ConfettiScreenState(); } class _ConfettiScreenState extends State\u0026lt;ConfettiScreen\u0026gt; { late ConfettiController _controller; @override void initState() { super.initState(); _controller = ConfettiController(duration: Duration(seconds: 4)); _controller.play(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Center( child: ConfettiWidget( confettiController: _controller, blastDirectionality: BlastDirectionality.explosive, numberOfParticles: 100, maxBlastForce: 40, emissionFrequency: 0.05 ) ); } } This creates a centered confetti explosion.\nConfetti explosion animation in a Flutter app\nHere is a machine gun like confetti animation:\nimport \u0026#39;package:confetti/confetti.dart\u0026#39;; import \u0026#39;package:flutter/widgets.dart\u0026#39;; class ConfettiScreen extends StatefulWidget { const ConfettiScreen({super.key}); @override State\u0026lt;ConfettiScreen\u0026gt; createState() =\u0026gt; _ConfettiScreenState(); } class _ConfettiScreenState extends State\u0026lt;ConfettiScreen\u0026gt; { late ConfettiController _controller; @override void initState() { super.initState(); _controller = ConfettiController(duration: Duration(seconds: 4)); _controller.play(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Align( alignment: Alignment.centerLeft, child: ConfettiWidget( confettiController: _controller, blastDirectionality: BlastDirectionality.directional, blastDirection: 0, numberOfParticles: 1, maxBlastForce: 100, minBlastForce: 99, emissionFrequency: 1, gravity: 0.01) ); } } Confetti machine gun animation in a Flutter app\nOr, if you want it more gentle, here is a subtle snowfall confetti animation:\nimport \u0026#39;package:confetti/confetti.dart\u0026#39;; import \u0026#39;package:flutter/widgets.dart\u0026#39;; import \u0026#39;dart:math\u0026#39; as math; class ConfettiScreen extends StatefulWidget { const ConfettiScreen({super.key}); @override State\u0026lt;ConfettiScreen\u0026gt; createState() =\u0026gt; _ConfettiScreenState(); } class _ConfettiScreenState extends State\u0026lt;ConfettiScreen\u0026gt; { late ConfettiController _controller; @override void initState() { super.initState(); _controller = ConfettiController(duration: Duration(seconds: 4)); _controller.play(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Align( alignment: Alignment.topCenter, child: ConfettiWidget( confettiController: _controller, blastDirectionality: BlastDirectionality.directional, blastDirection: math.pi / 2, numberOfParticles: 1, maxBlastForce: 2, minBlastForce: 1, emissionFrequency: 1, gravity: 0.05, particleDrag: 0.1, ), ); } } Confetti snowfall animation in a Flutter app\nIt’s a great package for all kinds of confetti animations!\nConclusion In this article, you learned how to implement simple confetti animations in Flutter apps. The proposed package is reliable and provides lots of configuration options to make the animation look the way you want it to be.\n","permalink":"https://quickcoder.org/posts/confetti-animations-in-flutter-apps/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eCelebrating user achievements is a great way to boost engagement in your Flutter apps. In this article, we’ll discover how to add simple confetti animations in Flutter apps . But since we are lazy developers, we use a package instead of creating the animation on our own. With the code examples and demo videos, you’ll be ready to blast your users away with confetti!\u003c/p\u003e\n\u003ch1 id=\"animation\"\u003eAnimation\u003c/h1\u003e\n\u003cp\u003eA proper confetti animation can be a challenge. Lucky for us, there is a \u003ca href=\"https://pub.dev/packages/confetti\"\u003epackage on pub.dev\u003c/a\u003e that helps us here.\u003c/p\u003e","title":"Easy Confetti Animations In Flutter Apps"},{"content":"Introduction In this article, we explore how to build a modern Flutter app with user authentication powered by Firebase Authentication. You’ll learn how to implement account management methods with Firebase Authentication in the most simple way. Check out the demo application and see how everything comes together.\nThis article covers the following use cases with Flutter code examples for Firebase Authentication:\nRegistration with email and password\nUsers can create an account by providing an email address and a password.\nSign in with email and password\nUsers can sign in with email and password after account creation.\nEmail address confirmation process\nTo verify the email address, users need to click a confirmation link in an email sent by the system.\nChange email address\nIn case the email address is no longer valid anymore, users can update it on their own.\nChange password\nUpdating their passwords can also be achieved for all users with minimal effort.\nForgot password process\nSometimes, people forget their passwords. Here is how you can give them the option to reset it.\nDelete account\nWith this option, users can delete their accounts. In the background, linked data like Firestore collection or Storage buckets can be deleted by a Cloud Function.\nLet’s get ahead with a Flutter app and how to implement account management methods with Firebase Authentication for free!\n💡 Tip\nThe entire system will work on Android, iOS, Web, MacOS, and Windows!\nFirebase preparations We need a working Firebase project that is linked with a Flutter app. Here are the quick steps:\nCreate a Flutter app (instructions)\nCreate a Firebase project (instructions)\nLink it with your Flutter app (instructions)\nEnable email provider in Firebase Authentication (instructions)\nPackages firebase_core and firebase_auth\nEverything ready? Then, let’s move on!\nRegistration Obviously, the first step for a user is to create an account. Here is what you need to do.\nFirst, we need a simple form with input fields for email and password. It could look like this:\nA simple registration form with email and password fields. Firebase expects an email and a password, so the confirmation field here is only to prevent typos of the users.\nWhen clicking the button, the following code is executed to create a new account in Firebase Authentication:\nawait FirebaseAuth.instance.createUserWithEmailAndPassword( email: _emailController.text.trim(), password: _passwordController.text.trim(), ); As a result, you get a UserCredential object and you can always access the current User object with\nawait FirebaseAuth.instance.currentUser; After the registration, the user is also in a logged in state.\nLogin Signing in is the bread-and-butter task for every user. Let’s see how we can accomplish this.\nAgain, we need a form where the user can input the data.\nA simple login form with email and password fields. There are also links to the registration page and the forgot password page.\nThen, we run this code after a button click:\nawait FirebaseAuth.instance.signInWithEmailAndPassword( email: _emailController.text.trim(), password: _passwordController.text.trim(), ); Once again, you get a UserCredential object as a result and you can access the current User object with\nawait FirebaseAuth.instance.currentUser; Email verification Sometimes, you want to make sure, users use valid email accounts. Here is how you can send a confirmation email and how you can check if the user clicked the link in it.\nFirst, we need to send a verification email to the current user:\nawait FirebaseAuth.instance.currentUser?.sendEmailVerification(); Verification email to prove that an address is real. By clicking the link, the verification is completed.\nWhen the user clicks the link, the verification is performed. The status is a property of the User object called emailVerified. It will be set to true when the verification was successful.\nChange email Switching to a new email doesn’t need to be complicated. Here is the form:\nForm to update the email address. The password is required in case the sign in was too long ago (roughly more than 5 minutes). In that case, a reauthentication is needed to change the email address.\nAnd here is the code:\n// assumption: User entered the password for reauthentication final credential = EmailAuthProvider.credential( email: user.email!, password: _passwordController.text.trim(), ); await user.reauthenticateWithCredential(credential); final newEmail = _emailController.text.trim(); await user.verifyBeforeUpdateEmail(newEmail); You’ll receive this email to your new account to complete the change:\nConfirmation email before a change. Click the link to confirm or ignore the email if you don\u0026rsquo;t want a change.\nWhen you click the link, you should see the following eventually:\nConfirmation that your email account has been verified and changed\nThis is a security-sensitive feature. Check the section about Reauthentication for more details. You also get a confirmation email to your old email account with a recovery link in case you didn’t initiate the change and want to revert it.\nConfirmation email after a change. Use the recovery link if the change wasn’t initiated by you and the state will be reverted.\nChange password Password changes are rare but the possibility should always be available.\nHere is an example form:\nForm to update the password. The old password is required in case the sign in was too long ago (roughly more than 5 minutes). In that case, a reauthentication is needed to change the password.\nAnd this is the corresponding code:\nfinal credential = EmailAuthProvider.credential( email: user.email!, password: _currentPasswordController.text.trim(), ); await user.reauthenticateWithCredential(credential); await user.updatePassword(_newPasswordController.text.trim()); The change will be effective immediately. When you sign in the next time, you can use your new password.\nThis is a security-sensitive feature. Check the section about Reauthentication for more details.\nForgot password Give your users an option to reset their passwords in case they need it.\nA simple form to recover a forgotten password. An email is sent to the entered account with instructions on how to set a new password.\nAgain, the code is pretty simple:\nawait FirebaseAuth.instance.sendPasswordResetEmail( email: _emailController.text.trim(), ); Once again, this will trigger an email with a link to click. Then, you should see this form:\nChoose a new password for your account\nEnter a new password, hit the button, and you are done!\nDelete account It’s always a good feeling when you are in control as a user. An option to delete an account is an easy feature and also required by many data protection regulations around the world.\nHere is how to delete a user (a sign out is also performed during the process):\nawait FirebaseAuth.instance.currentUser?.delete(); This is a security-sensitive feature. Check the section about Reauthentication for more details.\nA good follow-up is a Firebase Cloud Function that listens to the deletion and cleans up other Firebase resources like Firestore collections or Storage buckets.\nReauthentication Security-sensitive functions like changing passwords, changing email addresses, or deleting accounts require recent authentication data. If you call any of those functions and the last sign in is too far in the past (roughly 5 minutes seems to be the threshold), Firebase will return a requires-recent-login error code. In that case, ask the user to reauthenticate and execute the operation afterwards again.\n// prompt user to input email and password again final String email = ...; final String password = ...; final cred = EmailAuthProvider.credential(email: email, password: password); await FirebaseAuth.instance.currentUser?.reauthenticateWithCredential(cred); There is no option to check if reauthentication is necessary for an operation. You can only trigger the sensitive function and check the result.\nBonus: Configure password rules To enforce password guidelines by Firebase, go to Build \u0026gt; Authentication \u0026gt; Settings in your Firebase dashboard. There are several configuration options from minimal password length to required numbers or special characters. You can even force users to upgrade their current password on their next sign in.\nConfigure password rules for users in Firebase Authentication\nBonus: Configure email templates Firebase offers email templates that can partially be configured. Go to Build \u0026gt; Authentication \u0026gt; Templates for the customization. Some options are disabled, but you can change the subject or the language of the templates, for example.\nThere is also a setting to use a custom SMTP server instead of the built-in email service from Firebase.\nFirebase Authentication email templates for different processes like email verification or password reset\nBonus: Source code Follow this link to get the entire source code from this article. Check out the GitHub repository.\nConclusion In this article, we explored how to build a modern Flutter app with user authentication powered by Firebase Authentication. You learnt how to implement account management methods with Firebase Authentication in the most simple way. Together with the companion app and the detailed descriptions, you should be able to implement everything on your own.\n","permalink":"https://quickcoder.org/posts/how-to-implement-account-management-methods-with-firebase-authentication/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eIn this article, we explore how to build a modern Flutter app with user authentication powered by Firebase Authentication. You’ll learn how to implement account management methods with Firebase Authentication in the most simple way. Check out the demo application and see how everything comes together.\u003c/p\u003e\n\u003cp\u003eThis article covers the following use cases with Flutter code examples for Firebase Authentication:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eRegistration with email and password\u003c/strong\u003e\u003cbr\u003e\nUsers can create an account by providing an email address and a password.\u003c/p\u003e","title":"How To Implement Account Management Methods With Firebase Authentication"},{"content":"Introduction Here is a short guide about how to show test coverage of a Flutter app in Visual Studio Code. With code coverage, you can identify parts of your app that aren\u0026rsquo;t tested yet. However, it doesn\u0026rsquo;t tell you if your tests are good or your app is free of bugs!\nWe are going to use Visual Studio Code and two free extensions from the Visual Studio Marketplace:\nFlutter Coverage Extension to show code coverage for every file\nCoverage Gutters Extension to show covered and uncovered lines\nThe test subject I created a simple class called login_service.dart. It allows logging in, logging out, and checking the current state (logged in or not). It’s very basic and has flaws (like hard-coded user name and password) but will be enough to demonstrate how coverage works.\nclass LoginService { final String _expectedPassword = \u0026#34;abc123!?=\u0026#34;; final String _expectedUser = \u0026#34;superUser\u0026#34;; bool _isLoggedIn = false; void login(String? user, String? password) { if (_isLoggedIn) throw Exception(\u0026#34;Please log out first\u0026#34;); if (user != _expectedUser) throw Exception(\u0026#34;Wrong user name\u0026#34;); if (password != _expectedPassword) throw Exception(\u0026#34;Wrong password\u0026#34;); _isLoggedIn = true; } void logout() { _isLoggedIn = false; } bool isLoggedIn() { return _isLoggedIn; } } The unit tests Furthermore, we have some unit tests to cover all possible statements of the LoginService. You can see it in the file below.\nimport \u0026#39;package:flutter_test/flutter_test.dart\u0026#39;; import \u0026#39;package:flutter_test_coverage/login_service.dart\u0026#39;; void main() { test(\u0026#34;Verify failed login with wrong user\u0026#34;, () { var _sut = LoginService(); expect(() =\u0026gt; _sut.login(\u0026#34;user\u0026#34;, \u0026#34;abc123!?=\u0026#34;), throwsException); expect(_sut.isLoggedIn(), isFalse); }); test(\u0026#34;Verify failed login with wrong password\u0026#34;, () { var _sut = LoginService(); expect(() =\u0026gt; _sut.login(\u0026#34;superUser\u0026#34;, \u0026#34;password\u0026#34;), throwsException); expect(_sut.isLoggedIn(), isFalse); }); test(\u0026#34;Verify logout before login\u0026#34;, () { var _sut = LoginService(); _sut.login(\u0026#34;superUser\u0026#34;, \u0026#34;abc123!?=\u0026#34;); expect(_sut.isLoggedIn(), isTrue); expect(() =\u0026gt; _sut.login(\u0026#34;superUser\u0026#34;, \u0026#34;abc123!?=\u0026#34;), throwsException); expect(_sut.isLoggedIn(), isTrue); }); test(\u0026#34;Verify successful login\u0026#34;, () { var _sut = LoginService(); _sut.login(\u0026#34;superUser\u0026#34;, \u0026#34;abc123!?=\u0026#34;); expect(_sut.isLoggedIn(), isTrue); }); test(\u0026#34;Verify logout works\u0026#34;, () { var _sut = LoginService(); expect(_sut.isLoggedIn(), isFalse); _sut.login(\u0026#34;superUser\u0026#34;, \u0026#34;abc123!?=\u0026#34;); expect(_sut.isLoggedIn(), isTrue); _sut.logout(); expect(_sut.isLoggedIn(), isFalse); _sut.logout(); expect(_sut.isLoggedIn(), isFalse); }); } Collect coverage information Flutter has a built-in command to collect coverage information while executing all tests. So you just need to open a shell of your choice, navigate to the project root folder, and execute the following command flutter test --coverage.\nThis will execute all tests in your project and create a file lcov.info which contains the coverage information. The extensions you installed earlier rely on the file to visualize the information.\nShow covered lines in the editor After the coverage information has been collected open a file that has been tested (in this example the login_service.dart file). You should see something like this.\nCovered lines by tests in Visual Studio Code\nA green line means that there is a test executing this code line while a red line means that there is no test executing this code line. This class has a coverage of 100% as all unmarked lines (like field declarations) are not measured by the Flutter tool.\n❗\nWarning\nBe aware that the colors don’t indicate if a test passes or not. They just tell you if there is any test for a line.\nIf you can’t see any colorings check the status bar and enable the coverage gutters.\nCoverage display is not enabled, click to enable\nCoverage display is enabled, click to disable\nShow coverage information per file In bigger projects, you’d like to have an overview of those files that have sufficient test coverage and those that don’t. To solve this problem you can use the Flutter Coverage extension. It shows the coverage percentage of every dart file in your project. Go to your Test Explorer in VS Code and open the Flutter Coverage drawer. You should see something like this.\nTest coverage per file with Flutter Coverage in VS Code\nThe coverage of the login_service.dart class is 100% as we have concluded before. Especially if your project gets bigger and bigger, this view will be quite handy to identify insufficiently tested classes.\nCredits If you like the extensions, the developers appreciate a rating or any feedback.\nReview Coverage Gutters\nReview Flutter Coverage\nSource code You can find the source code on GitHub.\nConclusion In this article, you saw how to show test coverage of a Flutter app in Visual Studio Code. All it takes are two extensions and the built-in Flutter tools. With code coverage, you can better identify what parts of your app need more testing.\n","permalink":"https://quickcoder.org/posts/flutter-test-coverage/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere is a short guide about how to show test coverage of a Flutter app in Visual Studio Code. With code coverage, you can identify parts of your app that aren\u0026rsquo;t tested yet. However, it doesn\u0026rsquo;t tell you if your tests are good or your app is free of bugs!\u003c/p\u003e\n\u003cp\u003eWe are going to use \u003cstrong\u003eVisual Studio Code\u003c/strong\u003e and two free extensions from the Visual Studio Marketplace:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://marketplace.visualstudio.com/items?itemName=Flutterando.flutter-coverage\"\u003eFlutter Coverage\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cfigure\u003e\n\u003cp\u003e\u003cimg alt=\"Extension to show code coverage for every file\" loading=\"lazy\" src=\"/posts/flutter-test-coverage/images/ext1.png\"\u003e\u003c/p\u003e","title":"How To Show Test Coverage Of A Flutter App In Visual Studio Code"},{"content":"Introduction Mobile apps often use bottom navigation. You can reach all entries with one hand and it’s the same for Android and iOS. For Flutter apps, there is the widget BottomNavigationBar to accomplish this. In this article, I’ll share how to use BottomNavigationBar in Flutter apps.\nWe start with this standard implementation:\nclass MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: \u0026#39;Flutter Demo\u0026#39;, debugShowCheckedModeBanner: false, theme: ThemeData( colorScheme: ColorScheme.fromSeed( seedColor: Colors.deepPurple, ), ), home: const MainPage(), ); } } class MainPage extends StatefulWidget { const MainPage({super.key}); @override State\u0026lt;MainPage\u0026gt; createState() =\u0026gt; _MainPageState(); } class _MainPageState extends State\u0026lt;MainPage\u0026gt; { final List\u0026lt;String\u0026gt; pages = [\u0026#34;Home\u0026#34;, \u0026#34;Search\u0026#34;, \u0026#34;Settings\u0026#34;]; String _selectedPage = \u0026#34;Home\u0026#34;; @override Widget build(BuildContext context) { return Scaffold( bottomNavigationBar: BottomNavigationBar( currentIndex: pages.indexOf(_selectedPage), onTap: (index) =\u0026gt; setState(() { _selectedPage = pages[index]; }), items: [ BottomNavigationBarItem(icon: Icon(Icons.home), label: pages[0]), BottomNavigationBarItem(icon: Icon(Icons.search), label: pages[1]), BottomNavigationBarItem(icon: Icon(Icons.settings), label: pages[2]), ], ), body: Center(child: Text(_selectedPage)), ); } } There are 3 item that set the page to the corresponding entry when they are clicked. Every item consists of an icon and a label.\nAnd it looks like this\nBottomNavigationBar in a Flutter app\nYou can already see that the selected item is slightly bigger than the others. Let’s start changing the style.\nChange item colors First, we start changing the colors. By default, the primary color is used to highlight the selected entry. To set another color try this\n@override Widget build(BuildContext context) { return MaterialApp( title: \u0026#39;Flutter Demo\u0026#39;, debugShowCheckedModeBanner: false, theme: ThemeData( colorScheme: ColorScheme.fromSeed( seedColor: Colors.deepPurple, ), bottomNavigationBarTheme: BottomNavigationBarThemeData( selectedItemColor: Colors.blue, unselectedItemColor: Colors.blueGrey, ), ), home: const MainPage(), ); } You can either set the colors for the entire items or use selectedIconTheme/ unselectedIconTheme and selectedLabelStyle/unselectedLabelStyle to have even more control.\nBottomNavigationBar with non-standard colors for selected and unselected items\nMake items have the same size Let’s make all icons and texts always have the same size.\n@override Widget build(BuildContext context) { return MaterialApp( title: \u0026#39;Flutter Demo\u0026#39;, debugShowCheckedModeBanner: false, theme: ThemeData( colorScheme: ColorScheme.fromSeed( seedColor: Colors.deepPurple, ), bottomNavigationBarTheme: BottomNavigationBarThemeData( selectedItemColor: Colors.blue, unselectedItemColor: Colors.blueGrey, selectedLabelStyle: TextStyle(fontSize: 16), unselectedLabelStyle: TextStyle(fontSize: 16), selectedIconTheme: IconThemeData(size: 24), unselectedIconTheme: IconThemeData(size: 24), ), ), home: const MainPage(), ); } In the app, all texts and icons have the same size now. It doesn’t matter if an entry is selected or not.\nBottomNavigationBar with equally sized items\nHide labels You can also hide the labels and make the icons bigger. Try this\n@override Widget build(BuildContext context) { return MaterialApp( title: \u0026#39;Flutter Demo\u0026#39;, debugShowCheckedModeBanner: false, theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), bottomNavigationBarTheme: BottomNavigationBarThemeData( selectedItemColor: Colors.blue, unselectedItemColor: Colors.blueGrey, selectedLabelStyle: TextStyle(fontSize: 16), unselectedLabelStyle: TextStyle(fontSize: 16), selectedIconTheme: IconThemeData(size: 32), unselectedIconTheme: IconThemeData(size: 32), showSelectedLabels: false, showUnselectedLabels: false, ), ), home: const MainPage(), ); } Possible, but I personally don’t prefer this approach. Icons only are usually not that good to identify the purpose of an item.\nBottomNavigationBar with hidden labels\nHighlight the selected item By default, the selected item was a bit larger than the others. But there is another option: BottomNavigationBarType. Here is how it works:\n@override Widget build(BuildContext context) { return MaterialApp( title: \u0026#39;Flutter Demo\u0026#39;, debugShowCheckedModeBanner: false, theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), bottomNavigationBarTheme: BottomNavigationBarThemeData( selectedItemColor: Colors.blue, unselectedItemColor: Colors.blueGrey, selectedLabelStyle: TextStyle(fontSize: 16), unselectedLabelStyle: TextStyle(fontSize: 16), selectedIconTheme: IconThemeData(size: 32), unselectedIconTheme: IconThemeData(size: 32), type: BottomNavigationBarType.shifting, ), ), home: const MainPage(), ); } The default value is fixed. When set to shifting, the selected item grabs a little more space than the other and the label is automatically hidden if you don’t explicitly set showUnselectedLabels to true.\nHere is how it looks. I personally don’t like it that way.\nBottomNavigationBar with enlarged selected item\nChange elevation Did you notice the fine line above the navigation bar?\nIt’s there because the widget is elevated. You can increase the value to make the gradient (it’s not a line but rather a gradient) larger or set it to 0 to have a clean area.\n@override Widget build(BuildContext context) { return MaterialApp( title: \u0026#39;Flutter Demo\u0026#39;, debugShowCheckedModeBanner: false, theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), bottomNavigationBarTheme: BottomNavigationBarThemeData( selectedItemColor: Colors.blue, unselectedItemColor: Colors.blueGrey, selectedLabelStyle: TextStyle(fontSize: 16), unselectedLabelStyle: TextStyle(fontSize: 16), selectedIconTheme: IconThemeData(size: 32), unselectedIconTheme: IconThemeData(size: 32), elevation: 0 ), ), home: const MainPage(), ); } It depends on what you like more!\nBottomNavigationBar without any elevation\nChange background color Of course, you can change the background color if you like. Have a look:\n@override Widget build(BuildContext context) { return MaterialApp( title: \u0026#39;Flutter Demo\u0026#39;, debugShowCheckedModeBanner: false, theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), bottomNavigationBarTheme: BottomNavigationBarThemeData( selectedItemColor: Colors.blue, unselectedItemColor: Colors.blueGrey, selectedLabelStyle: TextStyle(fontSize: 16), unselectedLabelStyle: TextStyle(fontSize: 16), selectedIconTheme: IconThemeData(size: 32), unselectedIconTheme: IconThemeData(size: 32), elevation: 0 backgroundColor: Colors.amber ), ), home: const MainPage(), ); } 💡 Tip\nNote that this does not work if the type is set to shifting! The color is then ignored.\nBottomNavigationBar with different background color\nTesting When writing widget tests for your app, be aware that you cannot find the type BottomNavigationBarItem. Here is an example:\nIn widget tests, BottomNavigationBarItems cannot be found and the test fails.\nAs a workaround, you can check for the labels with find.text or icons with find.byIcon of your entries. Another approach is to assign a Key to every item and check for the key with find.byKey.\nConclusion These are the basics about how to use BottomNavigationBar in Flutter apps. Testing is a bit tricky but apart from that, it’s an easy to use and common widget in mobile apps.\n","permalink":"https://quickcoder.org/posts/how-to-use-bottomnavigationbar-in-flutter-apps/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eMobile apps often use bottom navigation. You can reach all entries with one hand and it’s the same for Android and iOS. For Flutter apps, there is the widget \u003ca href=\"https://api.flutter.dev/flutter/material/BottomNavigationBar-class.html\"\u003eBottomNavigationBar\u003c/a\u003e to accomplish this. In this article, I’ll share how to use BottomNavigationBar in Flutter apps.\u003c/p\u003e\n\u003cp\u003eWe start with this standard implementation:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-dart\" data-lang=\"dart\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eMyApp\u003c/span\u003e \u003cspan class=\"kd\"\u003eextends\u003c/span\u003e \u003cspan class=\"n\"\u003eStatelessWidget\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"n\"\u003eMyApp\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\u003cspan class=\"k\"\u003esuper\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"err\"\u003e@\u003c/span\u003e\u003cspan class=\"n\"\u003eoverride\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eWidget\u003c/span\u003e \u003cspan class=\"n\"\u003ebuild\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eBuildContext\u003c/span\u003e \u003cspan class=\"n\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eMaterialApp\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nl\"\u003etitle:\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;Flutter Demo\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nl\"\u003edebugShowCheckedModeBanner:\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nl\"\u003etheme:\u003c/span\u003e \u003cspan class=\"n\"\u003eThemeData\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nl\"\u003ecolorScheme:\u003c/span\u003e \u003cspan class=\"n\"\u003eColorScheme\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efromSeed\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          \u003cspan class=\"nl\"\u003eseedColor:\u003c/span\u003e \u003cspan class=\"n\"\u003eColors\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edeepPurple\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nl\"\u003ehome:\u003c/span\u003e \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"n\"\u003eMainPage\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eMainPage\u003c/span\u003e \u003cspan class=\"kd\"\u003eextends\u003c/span\u003e \u003cspan class=\"n\"\u003eStatefulWidget\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"n\"\u003eMainPage\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\u003cspan class=\"k\"\u003esuper\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"err\"\u003e@\u003c/span\u003e\u003cspan class=\"n\"\u003eoverride\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eState\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eMainPage\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ecreateState\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003e_MainPageState\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003e_MainPageState\u003c/span\u003e \u003cspan class=\"kd\"\u003eextends\u003c/span\u003e \u003cspan class=\"n\"\u003eState\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eMainPage\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003efinal\u003c/span\u003e \u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003eString\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003epages\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Home\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Search\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Settings\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kt\"\u003eString\u003c/span\u003e \u003cspan class=\"n\"\u003e_selectedPage\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Home\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"err\"\u003e@\u003c/span\u003e\u003cspan class=\"n\"\u003eoverride\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eWidget\u003c/span\u003e \u003cspan class=\"n\"\u003ebuild\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eBuildContext\u003c/span\u003e \u003cspan class=\"n\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eScaffold\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nl\"\u003ebottomNavigationBar:\u003c/span\u003e \u003cspan class=\"n\"\u003eBottomNavigationBar\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nl\"\u003ecurrentIndex:\u003c/span\u003e \u003cspan class=\"n\"\u003epages\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eindexOf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003e_selectedPage\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nl\"\u003eonTap:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eindex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003esetState\u003c/span\u003e\u003cspan class=\"p\"\u003e(()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e              \u003cspan class=\"n\"\u003e_selectedPage\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epages\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eindex\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nl\"\u003eitems:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          \u003cspan class=\"n\"\u003eBottomNavigationBarItem\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nl\"\u003eicon:\u003c/span\u003e \u003cspan class=\"n\"\u003eIcon\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIcons\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ehome\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"nl\"\u003elabel:\u003c/span\u003e \u003cspan class=\"n\"\u003epages\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e]),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          \u003cspan class=\"n\"\u003eBottomNavigationBarItem\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nl\"\u003eicon:\u003c/span\u003e \u003cspan class=\"n\"\u003eIcon\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIcons\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esearch\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"nl\"\u003elabel:\u003c/span\u003e \u003cspan class=\"n\"\u003epages\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"m\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e]),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          \u003cspan class=\"n\"\u003eBottomNavigationBarItem\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nl\"\u003eicon:\u003c/span\u003e \u003cspan class=\"n\"\u003eIcon\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIcons\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esettings\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"nl\"\u003elabel:\u003c/span\u003e \u003cspan class=\"n\"\u003epages\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"m\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e]),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e],\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nl\"\u003ebody:\u003c/span\u003e \u003cspan class=\"n\"\u003eCenter\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nl\"\u003echild:\u003c/span\u003e \u003cspan class=\"n\"\u003eText\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003e_selectedPage\u003c/span\u003e\u003cspan class=\"p\"\u003e)),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThere are 3 item that set the page to the corresponding entry when they are clicked. Every item consists of an icon and a label.\u003c/p\u003e","title":"How To Use BottomNavigationBar In Flutter Apps"},{"content":"Introduction In software projects, there is usually a project vision of what the end product should look like. When a developer starts working, there should be some kind of mockup (best case is a Figma file, but I have also seen pencil drawings…). In this article, I want to share my strategies of how to cut a design mockup into Flutter widgets.\nTo illustrate the process, let’s assume we have this template:\nWhatsapp screenshot of the UI\nThis is an old screenshot of the messenger app WhatsApp. We are going to analyze it, slice it into chunks, and find Flutter widgets to implement it. We don’t care about the full functionality like calling or sending messages. The UI is the only part that is interesting to us.\nAvailable widgets I always recommend starting with the base Flutter widgets. They are well-tested and have proven their reliability. Starting from that base, we build tailored widgets for our use cases.\nSometimes, it makes sense to check the official package repository pub.dev for alternatives to save time. But be cautious not to just use any widget because the repository is full of packages that only serve a specific purpose while having limitations in other areas. Regular updates, high download numbers, and active development are indicators for a solid candidate.\nFrom top to bottom I always go from top to bottom. Why?\nBecause that is the natural reading behavior of people. Of course, you can start from the bottom with your analysis, but it doesn’t feel natural to me.\nThe same rule applies for the horizontal direction: I go from left to right. But there is no harm in doing right to left. Totally fine and works with the same principles.\nChoose what you like best!\nTo follow along with this article about how to cut a design mockup into Flutter widgets, remember that we use a top-to-bottom and left-to-right structure.\nThe app bar At the top of practically every major app, there is an AppBar. It usually contains the title and several actions like searching or opening a menu.\nScreenshot of the WhatsApp app bar including a title, a search button, and a menu button\nIn our example, we have a title on the left and search and menu buttons on the right. Here is how you could do this in a Flutter app:\nScaffold( backgroundColor: Colors.white, appBar: AppBar( backgroundColor: Color(0xFF075E54), title: Text(\u0026#39;WhatsApp\u0026#39;, style: TextStyle(color: Colors.white)), actions: [ IconButton( icon: Icon(Icons.search), onPressed: () { // TODO: Implement search }, ), IconButton( icon: Icon(Icons.more_vert), onPressed: () { // TODO: Implement menu }, ), ], ) ) The code sets the title, adjusts some colors, and creates the actions as tappable IconButtons. The Scaffold acts as a container and makes it easy to create views.\nIf you add a Drawer widget to the Scaffold, the framework automatically adds it to the AppBar (but with a different icon).\nSince these are only screenshots, setting the correct margins, paddings, font faces, and sizes is tricky. When you have a proper Figma design, you can create pixel-perfect UIs based on that. For the sake of this example, we skip this step.\nThis is how the app can look like after adding the AppBar:\nScreenshot of the demo app after adding the app bar with title and action icons\nThe tab bar Next, we focus on the TabBar section. A lot is going on here. On the left, we have a camera icon button. Next to that, there is the TabBar widget with three headers and an optional unread notification count indicator.\nScreenshot of the WhatsApp tab bar including a photo button and three tab headers with optional notification counters\nCreate a Row To make this work, we can use a Row widget that contains the IconButton and the TabBar inside. A Row aligns widgets in a horizontal direction next to each other. The counterpart is a Column that does the same in a vertical direction.\nHere is how you could create the Row:\nScaffold( backgroundColor: Colors.white, appBar: AppBar( backgroundColor: Color(0xFF075E54), title: Text(\u0026#39;WhatsApp\u0026#39;, style: TextStyle(color: Colors.white)), actions: [ IconButton( icon: Icon(Icons.search, color: Colors.white), onPressed: () { // TODO: Implement search }, ), IconButton( icon: Icon(Icons.more_vert, color: Colors.white), onPressed: () { // TODO: Implement menu }, ), ], bottom: PreferredSize( preferredSize: Size.fromHeight(48), child: Container( color: Color(0xFF075E54), child: Row( children: [ IconButton( icon: Icon(Icons.camera_alt, color: Colors.white), onPressed: () { // TODO: Implement camera }, ), Expanded( child: Container(), // TODO: Add TabBar ), ], ), ), ), ); The bottom property is useful when you want to place widgets underneath the AppBar. It expects a PreferredSize widget to determine the height. You cannot set the height in the Container and skip the PreferredSize widget. This leads to a runtime exception.\nThe Expanded widget in the Row is needed because the Container doesn’t specify a width. The framework wouldn’t know how to arrange the children. With the Expanded, we tell it to give the child the maximum space available. This is also quite useful in responsive designs.\nHere is the current result:\nScreenshot of the demo app after adding a row below the app bar with an action icon and an empty placeholder for the tab bar widget\nCreate a TabBar The next step is to remove the placeholder and insert a TabBar widget. A TabBar uses a TabController, Tabs inside the TabBar, and a TabBarView. A Tab is the clickable header, a TabBarView shows the content of a Tab, and a TabBarController connects the elements and handles clicks and animations.\nIn addition, we use the Badge widget to display the unread notification count next to the title. To align text and counter horizontally, we use the Row widget once again.\nDefaultTabController( initialIndex: 2, length: 3, child: Scaffold( backgroundColor: Colors.white, appBar: AppBar( backgroundColor: Color(0xFF075E54), title: Text( \u0026#39;WhatsApp\u0026#39;, style: TextStyle(color: Colors.white), ), actions: [ IconButton( icon: Icon(Icons.search, color: Colors.white), onPressed: () { // TODO: Implement search }, ), IconButton( icon: Icon(Icons.more_vert, color: Colors.white), onPressed: () { // TODO: Implement menu }, ), ], bottom: PreferredSize( preferredSize: Size.fromHeight(48), child: Container( color: Color(0xFF075E54), child: Row( children: [ IconButton( icon: Icon(Icons.camera_alt, color: Colors.white), onPressed: () { // TODO: Implement camera }, ), Expanded( child: TabBar( indicatorColor: Colors.white, labelColor: Colors.white, unselectedLabelColor: Colors.white70, tabs: [ Tab( child: Row( mainAxisSize: MainAxisSize.min, spacing: 4, children: [ Text(\u0026#39;CHATS\u0026#39;), Badge( backgroundColor: Colors.white, label: Text( \u0026#39;3\u0026#39;, style: TextStyle( color: Color(0xFF075E54), fontSize: 11, fontWeight: FontWeight.bold, ), ), ), ], ), ), Tab(text: \u0026#39;STATUS\u0026#39;), Tab(text: \u0026#39;CALLS\u0026#39;), ], ), ), ], ), ), ), ), body: TabBarView( children: [ Center(child: Text(\u0026#39;Chats\u0026#39;)), Center(child: Text(\u0026#39;Status\u0026#39;)), Center(child: Text(\u0026#39;Calls\u0026#39;)), ], ), ), ); The entire Scaffold is wrapped in a DefaultTabController. Its length property is set to 3. This means we need 3 Tabs and 3 TabViews. A runtime error occurs if there are too many or too few items.\nInside the first Tab, there is the mentioned Row for the title and the counter. Notice the spacing property here. The value is added as horizontal spacing between all children inside the Row. A Column works the same way.\nLet’s have a look at the latest app update:\nScreenshot of the demo app after adding a row below the app bar with an action icon and a tab bar widget with 3 tabs and a notification counter\nLooks quite good already! We are nearly halfway through this article on how to cut a design mockup into Flutter widgets. Let’s continue with the call list.\nThe sticky call link The sticky call link looks like a list item, but doesn’t scroll. It always stays at the top when scrolling the call list. To make this work, we simply put this widget on top of the call list table. Screenshot of the WhatsApp sticky call link item\nThe best widget for the job is the ListTile. It’s very flexible, can show text in two rows, and supports leading and trailing icons.\nCheck out the next code step:\nDefaultTabController( initialIndex: 2, length: 3, child: Scaffold( backgroundColor: Colors.white, appBar: AppBar( backgroundColor: Color(0xFF075E54), title: Text(\u0026#39;WhatsApp\u0026#39;, style: TextStyle(color: Colors.white)), actions: [ IconButton( icon: Icon(Icons.search, color: Colors.white), onPressed: () { // TODO: Implement search functionality }, ), IconButton( icon: Icon(Icons.more_vert, color: Colors.white), onPressed: () { // TODO: Implement menu }, ), ], bottom: PreferredSize( preferredSize: Size.fromHeight(48), child: Container( color: Color(0xFF075E54), child: Row( children: [ IconButton( icon: Icon(Icons.camera_alt, color: Colors.white), onPressed: () { // TODO: Implement camera }, ), Expanded( child: TabBar( indicatorColor: Colors.white, labelColor: Colors.white, unselectedLabelColor: Colors.white70, tabs: [ Tab( child: Row( mainAxisSize: MainAxisSize.min, children: [ Text(\u0026#39;CHATS\u0026#39;), SizedBox(width: 4), Badge( backgroundColor: Colors.white, label: Text( \u0026#39;3\u0026#39;, style: TextStyle( color: Color(0xFF075E54), fontSize: 11, fontWeight: FontWeight.bold, ), ), ), ], ), ), Tab(text: \u0026#39;STATUS\u0026#39;), Tab(text: \u0026#39;CALLS\u0026#39;), ], ), ), ], ), ), ), ), body: TabBarView( children: [ Center(child: Text(\u0026#39;Chats\u0026#39;)), Center(child: Text(\u0026#39;Status\u0026#39;)), Center( child: Column( children: [ ListTile( tileColor: Colors.white, leading: CircleAvatar( backgroundColor: Color(0xFF075E54), child: Icon(Icons.link, color: Colors.white), ), title: Text(\u0026#39;Create call link\u0026#39;), subtitle: Text(\u0026#39;Share a link for your WhatsApp call\u0026#39;), ), ], ), ), ], ), ), ); The 3rd TabBarView has changed. It now contains a Column with a ListTile inside (and a list in the next step). The icon does not match 100% because WhatsApp doesn’t use the Material Design Icons provided by the Flutter framework. We also didn’t implement a onTap event. So, nothing happens when you tap the link.\nHere is a quick update of our progress:\nScreenshot of the demo app after adding the sticky call link to share with others\nIt’s getting real!\nThe call list After the sticky call link, we can focus on the call list.\nScreenshot of the WhatsApp call list with a header and multiple entries\nThere is a group header above the list. The question now is: Is this header sticky or does it scroll with the list?\nWhen it should always stay at the same position, we can do the same as with the sticky call link:\n// DefaultTabController, Scaffold, AppBar, ... omitted TabBarView( children: [ Center(child: Text(\u0026#39;Chats\u0026#39;)), Center(child: Text(\u0026#39;Status\u0026#39;)), Center( child: Column( children: [ // Sticky Create Call Link tile Container( color: Colors.white, child: ListTile( leading: CircleAvatar( backgroundColor: Color(0xFF075E54), child: Icon(Icons.link, color: Colors.white), ), title: Text(\u0026#39;Create call link\u0026#39;), subtitle: Text(\u0026#39;Share a link for your WhatsApp call\u0026#39;), ), ), // Section header: \u0026#34;Recent\u0026#34; Container( alignment: Alignment.centerLeft, padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Text( \u0026#39;Recent\u0026#39;, style: TextStyle( fontSize: 13, color: Colors.grey[600], fontWeight: FontWeight.w500, ), ), ), // Scrollable list of dummy entries Expanded( child: ListView.builder( itemCount: 20, itemBuilder: (context, index) { return SizedBox( height: 40, child: Text(\u0026#34;Contact $index\u0026#34;), ); }, ), ), ], ), ), ], ); There is now another Container underneath the sticky call link to display the group header. It is outside of the ListView (we’ll deal with the list entries in the next chapter), so it doesn’t scroll.\nDemo app with a group header that does not scroll with the list\nTo make the header scroll with the list, it needs to be inside the ListView. We can do this with a little trick because ListView doesn’t support grouped lists.\n// DefaultTabController, Scaffold, AppBar, ... omitted TabBarView( children: [ Center(child: Text(\u0026#39;Chats\u0026#39;)), Center(child: Text(\u0026#39;Status\u0026#39;)), Center( child: Column( children: [ // Sticky Create Call Link tile Container( color: Colors.white, child: ListTile( leading: CircleAvatar( backgroundColor: Color(0xFF075E54), child: Icon(Icons.link, color: Colors.white), ), title: Text(\u0026#39;Create call link\u0026#39;), subtitle: Text(\u0026#39;Share a link for your WhatsApp call\u0026#39;), ), ), // Scrollable list including the \u0026#34;Recent\u0026#34; header Expanded( child: ListView.builder( itemCount: 21, // 1 for the header + 20 entries itemBuilder: (context, index) { if (index == 0) { // Group header inside the scroll return Container( alignment: Alignment.centerLeft, padding: EdgeInsets.symmetric( horizontal: 16, vertical: 8, ), child: Text( \u0026#39;Recent\u0026#39;, style: TextStyle( fontSize: 13, color: Colors.grey[600], fontWeight: FontWeight.w500, ), ), ); } // Call entries final contactIndex = index - 1; return SizedBox( height: 40, child: Text(\u0026#34;Contact $contactIndex\u0026#34;), ); }, ), ), ], ), ), ], ); We add the group header as the first entry by checking the index argument. When it’s zero, we show the header. Otherwise, we show the call entries.\nDemo app with a group header that scrolls with the list\nThe package grouped_list is a popular alternative that supports grouped lists. You can always fall back to the package repository when the built-in widgets don’t suit your needs.\nThe call list entry The last step is to design a single call list entry.\nScreenshot of a single entry of the WhatsApp call list. There is a profile picture, a name, status information, and an icon involved.\nTo do this, we once again rely on the ListTile widget that we used before. Image on the left, two rows of text, and an action icon on the right. The lower text row is a bit more complex. It contains an icon, a number, and a relative timestamp. But there is nothing we can’t deal with.\nListTile( leading: CircleAvatar( backgroundColor: Colors.grey[300], child: Text( \u0026#39;S\u0026#39;, style: TextStyle(color: Colors.black), ), ), title: Text(\u0026#39;Sam Torrence\u0026#39;), subtitle: Row( children: [ Icon(Icons.link, color: Colors.green, size: 16), SizedBox(width: 4), Text(\u0026#39;(2)\u0026#39;, style: TextStyle(fontSize: 12)), SizedBox(width: 6), Text( \u0026#39;35 minutes ago\u0026#39;, style: TextStyle(fontSize: 12), ), ], ), trailing: Icon(Icons.call, color: Color(0xFF075E54)), ); Nothing special here, just filling a ListTile with data. Everything is hardcoded in the first step. We first want to make it work and improve later.\nHere is the final code for the entire view:\nDefaultTabController( initialIndex: 2, length: 3, child: Scaffold( backgroundColor: Colors.white, appBar: AppBar( backgroundColor: Color(0xFF075E54), title: Text(\u0026#39;WhatsApp\u0026#39;, style: TextStyle(color: Colors.white)), actions: [ IconButton( icon: Icon(Icons.search, color: Colors.white), onPressed: () { // TODO: Implement search functionality }, ), IconButton( icon: Icon(Icons.more_vert, color: Colors.white), onPressed: () { // TODO: Implement menu }, ), ], bottom: PreferredSize( preferredSize: Size.fromHeight(48), child: Container( color: Color(0xFF075E54), child: Row( children: [ IconButton( icon: Icon(Icons.camera_alt, color: Colors.white), onPressed: () { // TODO: Implement camera }, ), Expanded( child: TabBar( indicatorColor: Colors.white, labelColor: Colors.white, unselectedLabelColor: Colors.white70, tabs: [ Tab( child: Row( mainAxisSize: MainAxisSize.min, children: [ Text(\u0026#39;CHATS\u0026#39;), SizedBox(width: 4), Badge( backgroundColor: Colors.white, label: Text( \u0026#39;3\u0026#39;, style: TextStyle( color: Color(0xFF075E54), fontSize: 11, fontWeight: FontWeight.bold, ), ), ), ], ), ), Tab(text: \u0026#39;STATUS\u0026#39;), Tab(text: \u0026#39;CALLS\u0026#39;), ], ), ), ], ), ), ), ), body: TabBarView( children: [ Center(child: Text(\u0026#39;Chats\u0026#39;)), Center(child: Text(\u0026#39;Status\u0026#39;)), Center( child: Column( children: [ // Sticky Create Call Link tile Container( color: Colors.white, child: ListTile( leading: CircleAvatar( backgroundColor: Color(0xFF075E54), child: Icon(Icons.link, color: Colors.white), ), title: Text(\u0026#39;Create call link\u0026#39;), subtitle: Text(\u0026#39;Share a link for your WhatsApp call\u0026#39;), ), ), // Sticky \u0026#34;Recent\u0026#34; header Container( alignment: Alignment.centerLeft, padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Text( \u0026#39;Recent\u0026#39;, style: TextStyle( fontSize: 13, color: Colors.grey[600], fontWeight: FontWeight.w500, ), ), ), // Scrollable list of calls Expanded( child: ListView( children: [ // Sam Torrence ListTile( leading: CircleAvatar( backgroundColor: Colors.grey[300], child: Text( \u0026#39;S\u0026#39;, style: TextStyle(color: Colors.black), ), ), title: Text(\u0026#39;Sam Torrence\u0026#39;), subtitle: Row( children: [ Icon(Icons.link, color: Colors.green, size: 16), SizedBox(width: 4), Text(\u0026#39;(2)\u0026#39;, style: TextStyle(fontSize: 12)), SizedBox(width: 6), Text( \u0026#39;35 minutes ago\u0026#39;, style: TextStyle(fontSize: 12), ), ], ), trailing: Icon(Icons.call, color: Color(0xFF075E54)), ), // Steven \u0026amp; Valerie ListTile( leading: CircleAvatar( backgroundColor: Colors.grey[300], child: Text( \u0026#39;S\u0026#39;, style: TextStyle(color: Colors.black), ), ), title: Text(\u0026#39;Steven \u0026amp; Valerie\u0026#39;), subtitle: Row( children: [ Icon(Icons.link, color: Colors.green, size: 16), SizedBox(width: 6), Text( \u0026#39;Today 12:37\u0026#39;, style: TextStyle(fontSize: 12), ), ], ), trailing: Icon( Icons.videocam, color: Color(0xFF075E54), ), ), // Inaaya Mckee \u0026amp; 3 others ListTile( leading: CircleAvatar( backgroundColor: Colors.grey[300], child: Text( \u0026#39;I\u0026#39;, style: TextStyle(color: Colors.black), ), ), title: Text(\u0026#39;Inaaya Mckee \u0026amp; 3 others\u0026#39;), subtitle: Row( children: [ Icon(Icons.link, color: Colors.red, size: 16), SizedBox(width: 6), Text( \u0026#39;Today 10:42\u0026#39;, style: TextStyle(fontSize: 12), ), ], ), trailing: Icon( Icons.videocam, color: Color(0xFF075E54), ), ), // Karim Nassar ListTile( leading: CircleAvatar( backgroundColor: Colors.grey[300], child: Text( \u0026#39;K\u0026#39;, style: TextStyle(color: Colors.black), ), ), title: Text(\u0026#39;Karim Nassar\u0026#39;), subtitle: Row( children: [ Icon(Icons.link, color: Colors.red, size: 16), SizedBox(width: 4), Text(\u0026#39;(3)\u0026#39;, style: TextStyle(fontSize: 12)), SizedBox(width: 6), Text( \u0026#39;Yesterday 16:03\u0026#39;, style: TextStyle(fontSize: 12), ), ], ), trailing: Icon(Icons.call, color: Color(0xFF075E54)), ), // Adriana Driscoll ListTile( leading: CircleAvatar( backgroundColor: Colors.grey[300], child: Text( \u0026#39;A\u0026#39;, style: TextStyle(color: Colors.black), ), ), title: Text(\u0026#39;Adriana Driscoll\u0026#39;), subtitle: Row( children: [ Icon(Icons.link, color: Colors.green, size: 16), SizedBox(width: 6), Text( \u0026#39;23 September 12:51\u0026#39;, style: TextStyle(fontSize: 12), ), ], ), trailing: Icon( Icons.videocam, color: Color(0xFF075E54), ), ), ], ), ), ], ), ), ], ), ), ); And here is what the result looks like:\nFull recreation (left) of the design mockup (right). It’s not 100% perfect and accurate, but it can be enhanced with little effort.\nAs you can see, it’s not perfect yet. But I hope you now have a better understanding of how to cut a design mockup into Flutter widgets from this article.\nNext steps You have successfully built a user interface according to a mockup. But what to do next?\nThere are several optimization steps that you can take.\nYour code is probably one huge widget. Make it smaller! Extract parts in other reusable widgets and reference them. The code contains a lot of theme information. An improvement would be to use a ThemeData class in your MaterialApp widget and put all style information there (fonts, colors, margins, paddings, …). For multi-language apps, consider localizing your hardcoded strings. Make fixed values dynamic. For example, the counter value is currently fixed. Find a way to get the value at runtime. Introduce constants for reused values like fontSizeLarge or gapXS. And don’t forget to write tests for your widget! All of this is an iterative process. First, you recreate the mockup and then you refactor. When you are more experienced, you’ll probably already include some optimizations during the creation process.\nConclusion In this article, I shared my strategies of how to cut a design mockup into Flutter widgets. My strategy involves going from top to bottom and identifying basic Flutter widgets that the framework already provides. I then customize them to match the template. If you struggle with this, then maybe it’s an option to talk to the UX designer to revisit the mockup for easier implementation.\n","permalink":"https://quickcoder.org/posts/how-to-cut-a-design-mockup-into-flutter-widgets/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eIn software projects, there is usually a project vision of what the end product should look like. When a developer starts working, there should be some kind of mockup (best case is a Figma file, but I have also seen pencil drawings…). In this article, I want to share my strategies of how to cut a design mockup into Flutter widgets.\u003c/p\u003e\n\u003cp\u003eTo illustrate the process, let’s assume we have this template:\u003c/p\u003e","title":"How To Cut A Design Mockup Into Flutter Widgets"},{"content":"Introduction I introduced an AbsorbPointer widget which broke my app but the tests to prevent exactly this didn’t fail. Here is why AbsorbPointers are dangerous in Flutter widget tests.\nOnce again, I stumbled across a weird issue with Flutter widget tests. They are not as reliable as you might think. This time I found a problem with a combination of AbsorbPointer and TextField.\nHere is a pretty simple example:\n@override Widget build(BuildContext context) { return AbsorbPointer(absorbing: true, child: TextField()); } We have a TextField widget wrapped inside an AbsorbPointer widget. The purpose of the AbsorbPointer is to prevent the TextField from getting focused. The keyboard doesn’t open and you cannot enter text.\nWhen you launch this on any platform (mobile, web desktop), you’ll notice that you cannot focus the input field. Works as expected!\nBut now, let’s write a simple widget test to ensure the correct functionality. Again some code:\ntestWidgets(\u0026#39;Absorb pointer test\u0026#39;, (WidgetTester tester) async { await tester.pumpWidget(const MyApp()); await tester.enterText(find.byType(TextField), \u0026#34;test\u0026#34;); await tester.pump(); expect(find.text(\u0026#34;test\u0026#34;), findsNothing); }); The test tries to enter text in the TextField widget and verifies that it cannot find the entered text. ❌ But this test fails!\nThe expect line fails because the test could find the text “test”.\nIf you try the same with another widget like an ElevatedButton, you don’t get this weird behavior when tapping it. Instead, the error says that your widget cannot receive pointer events which is correct.\nHere is the explanation from ChatGPT:\nWidget tests simulate interactions at a higher level and don’t rely on actual pointer events or the platform’s keyboard. In your real app, the AbsorbPointer truly blocks the pointer events from reaching the TextField (preventing focus and the keyboard from opening), but in the test environment, the simulated tap doesn’t go through the normal hit testing. Instead, it directly triggers the TextField’s focus logic, so your tests can still enter data.\nIn short: the AbsorbPointer stops real user taps, but the test framework’s simulated gestures bypass that, allowing your test to work as expected.\nI’d be cautious when using AbsorbPointer widgets in combination with widget tests. AIs seem happy to promote them when asked how to disable tap events on widgets. And since I couldn’t find any bug reports about this, they cannot know the issues.\nConclusion In this article, you saw why AbsorbPointers are dangerous in Flutter widget tests. It can happen that you change your code and break your app while your tests remain silent and keep working. This happened to me because I blindly trusted AI and my tests.\n","permalink":"https://quickcoder.org/posts/why-absorbpointers-are-dangerous-in-flutter-widget-tests/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eI introduced an AbsorbPointer widget which broke my app but the tests to prevent exactly this didn’t fail. Here is why AbsorbPointers are dangerous in Flutter widget tests.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/posts/a-short-excursion-into-the-pitfalls-of-flutter-widget-testing/\"\u003eOnce again\u003c/a\u003e, I stumbled across a weird issue with Flutter widget tests. They are not as reliable as you might think. This time I found a problem with a combination of \u003ccode\u003eAbsorbPointer\u003c/code\u003e and \u003ccode\u003eTextField\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eHere is a pretty simple example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-dart\" data-lang=\"dart\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e@\u003c/span\u003e\u003cspan class=\"n\"\u003eoverride\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eWidget\u003c/span\u003e \u003cspan class=\"n\"\u003ebuild\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eBuildContext\u003c/span\u003e \u003cspan class=\"n\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eAbsorbPointer\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nl\"\u003eabsorbing:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nl\"\u003echild:\u003c/span\u003e \u003cspan class=\"n\"\u003eTextField\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe have a \u003ccode\u003eTextField\u003c/code\u003e widget wrapped inside an \u003ccode\u003eAbsorbPointer\u003c/code\u003e widget. The purpose of the \u003ccode\u003eAbsorbPointer\u003c/code\u003e is to prevent the \u003ccode\u003eTextField\u003c/code\u003e from getting focused. The keyboard doesn’t open and you cannot enter text.\u003c/p\u003e","title":"Why AbsorbPointers Are Dangerous In Flutter Widget Tests"},{"content":"Introduction Here is how to write a proper Dart data model by hand, with various packages, and with AI assistance to make your life easier.\nWriting good data models is essential for every developer. In this article I want to show you how to write a proper Dart data model manually, with package support, through AI assistance, or with a web tool. Take this as a starting point to write better models in the future.\nWhat a proper data model should contain A data model usually has the following capabilities:\nContains all required properties\nThis should be obvious :)\nIs immutable\nImmutable means you cannot change the object once it is created. Mutable object tend to produce more errors if you are not careful enough and can introduce bugs more easily.\nOffers a copy option\nThis is a way to handle the immutability. Instead of changing a value in your original object, you create a copy of it with the changes.\nHas a serialization/deserialization feature\nWhen working with database or APIs, this is a must-have feature. JSON is the default data format of the internet but there are also other formats around like XML.\nHandles equality comparison\nTo know if two instances are equal or not is vital for many apps. By implementing a proper equality comparison, you make sure that your app doesn’t just compare memory addresses.\nHas additional constructors to create special objects like default ones\nA common use case is to create an empty object with all properties set to their default values. You can use this it instead of setting a reference to null.\nWhich of those features you need, always depends on your situation. Maybe you don’t need additional constructors or a copy method. Or maybe you need something that isn’t listed here. Always model your class so that it satisfies your needs.\nNow let’s see how to write a proper Dart data model manually, with packages, and with an AI prompt.\nBuild the model yourself The more features your model needs the less likely it is that you are writing the entire code. Packages and AI have proven to save you a lot of time and trouble. Anyway, here is how you would do it without any help.\nImmutable properties We start by defining immutable properties in our class. The keyword final is helpful here. Then we create a constructor to inject the values.\nHere is an example with only primitive data types:\nclass MyModel { final String id; final double price; final DateTime createdAt; final bool enabled; MyModel({ required this.id, required this.price, required this.createdAt, required this.enabled, }); } Copy option Since the model is immutable, we cannot change the values. One solution is to create a copy with the changed values.\nHere is how it could look like:\nclass MyModel { final String id; final double price; final DateTime createdAt; final bool enabled; MyModel({ required this.id, required this.price, required this.createdAt, required this.enabled, }); MyModel copyWith( String? newId, double? newPrice, DateTime? newCreatedAt, bool? newEnabled, ) { return MyModel( id: newId ?? id, price: newPrice ?? price, createdAt: newCreatedAt ?? createdAt, enabled: newEnabled ?? enabled, ); } } The copyWith method has all model properties as nullable arguments. If an argument is set, the value overwrites the current value in the new object.\nYou’ve probably already noticed that you need to adapt this method if you add or change a model property. This is one of the tasks that the packages and AI can take care of for you. Serialization and deserialization REST APIs or NoSQL databases usually work with JSON. To use them we need to convert our model in a JSON string (and reconvert it to a model when getting results back). That is what serialization is all about.\nHere is an example for our model:\nclass MyModel { // code parts omitted for brevity factory MyModel.fromJson(Map\u0026lt;String, dynamic\u0026gt; json) { return MyModel( id: json[\u0026#39;id\u0026#39;] as String, price: (json[\u0026#39;price\u0026#39;] as num).toDouble(), createdAt: DateTime.parse(json[\u0026#39;createdAt\u0026#39;] as String), enabled: json[\u0026#39;enabled\u0026#39;] as bool, ); } Map\u0026lt;String, dynamic\u0026gt; toJson() { return { \u0026#39;id\u0026#39;: id, \u0026#39;price\u0026#39;: price, \u0026#39;createdAt\u0026#39;: createdAt.toIso8601String(), \u0026#39;enabled\u0026#39;: enabled, }; } } There is a factory constructor that takes the decoded JSON response (from an API or database) and creates a model instance from it by parsing, converting, and mapping all JSON key-value pairs to model properties.\nThe toJson method is straightforward. Create a map, pass property names as map keys and property values as map values. That’s it.\n⚠️ Notice that the second type argument of the Map object is dynamic. This allows setting all kinds of data types but it can lead to runtime errors because the compiler cannot check for type errors.\nHere are two more robust versions for the fromJson constructor:\n// option 1: throw exception when key is missing or data type is wrong factory MyModel.fromJson(Map\u0026lt;String, dynamic\u0026gt; json) { if (!json.containsKey(\u0026#39;id\u0026#39;) || json[\u0026#39;id\u0026#39;] is! String) { throw FormatException(\u0026#39;Missing or invalid \u0026#34;id\u0026#34;: Expected a non-null String.\u0026#39;); } if (!json.containsKey(\u0026#39;price\u0026#39;) || json[\u0026#39;price\u0026#39;] is! num) { throw FormatException(\u0026#39;Missing or invalid \u0026#34;price\u0026#34;: Expected a non-null number.\u0026#39;); } if (!json.containsKey(\u0026#39;createdAt\u0026#39;) || json[\u0026#39;createdAt\u0026#39;] is! String) { throw FormatException(\u0026#39;Missing or invalid \u0026#34;createdAt\u0026#34;: Expected a non-null String.\u0026#39;); } if (!json.containsKey(\u0026#39;enabled\u0026#39;) || json[\u0026#39;enabled\u0026#39;] is! bool) { throw FormatException(\u0026#39;Missing or invalid \u0026#34;enabled\u0026#34;: Expected a boolean.\u0026#39;); } final createdAt = DateTime.tryParse(json[\u0026#39;createdAt\u0026#39;] as String); if (createdAt == null) { throw FormatException(\u0026#39;Invalid DateTime format for \u0026#34;createdAt\u0026#34;.\u0026#39;); } return MyModel( id: json[\u0026#39;id\u0026#39;] as String, price: (json[\u0026#39;price\u0026#39;] as num).toDouble(), createdAt: createdAt, enabled: json[\u0026#39;enabled\u0026#39;] as bool, ); } // option 2: set default values on error factory MyModel.fromJson(Map\u0026lt;String, dynamic\u0026gt; json) { return MyModel( id: json[\u0026#39;id\u0026#39;] is String ? json[\u0026#39;id\u0026#39;] as String : \u0026#39;\u0026#39;, price: json[\u0026#39;price\u0026#39;] is num ? (json[\u0026#39;price\u0026#39;] as num).toDouble() : 0.0, createdAt: json[\u0026#39;createdAt\u0026#39;] is String ? DateTime.tryParse(json[\u0026#39;createdAt\u0026#39;] as String) ?? DateTime(1970, 1, 1) : DateTime(1970, 1, 1), enabled: json[\u0026#39;enabled\u0026#39;] is bool ? json[\u0026#39;enabled\u0026#39;] as bool : false, ); } Again, you can see that there is a lot of boilerplate code and the amount grows the more properties your model has.\nEquality check To make equality checks work, we need to overwrite the equality operator == and the hashCode getter. Then, the compiler can decide if two instances are the same. Usually you would consider two instances the same if properties have the same values.\nHere is some example code:\nclass MyModel { final String id; final double price; final DateTime createdAt; final bool enabled; MyModel({ required this.id, required this.price, required this.createdAt, required this.enabled, }); // code parts omitted for brevity @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is MyModel \u0026amp;\u0026amp; other.id == id \u0026amp;\u0026amp; other.price == price \u0026amp;\u0026amp; other.createdAt == createdAt \u0026amp;\u0026amp; other.enabled == enabled; } @override int get hashCode =\u0026gt; id.hashCode ^ price.hashCode ^ createdAt.hashCode ^ enabled.hashCode; } Additional constructors I often use additional constructors to create special objects like an empty one with all properties set to their default values. It helps me to not rely on null values for example.\nHave a look at this code:\nclass MyModel { final String id; final double price; final DateTime createdAt; final bool enabled; MyModel({ required this.id, required this.price, required this.createdAt, required this.enabled, }); MyModel.empty() : this(id: \u0026#34;\u0026#34;, price: 0.0, createdAt: DateTime(1970, 1, 1), enabled: false); } I use a named constructor that redirects to the default constructor and sets default values. It’s a quick and handy way to create an empty object.\nSummary Now we have applied a lot of functionality to our model class. The final result looks something like this:\nclass MyModel { final String id; final double price; final DateTime createdAt; final bool enabled; MyModel({ required this.id, required this.price, required this.createdAt, required this.enabled, }); MyModel.empty() : this(id: \u0026#34;\u0026#34;, price: 0.0, createdAt: DateTime(1970, 1, 1), enabled: false); MyModel copyWith( String? newId, double? newPrice, DateTime? newCreatedAt, bool? newEnabled, ) { return MyModel( id: newId ?? id, price: newPrice ?? price, createdAt: newCreatedAt ?? createdAt, enabled: newEnabled ?? enabled, ); } factory MyModel.fromJson(Map\u0026lt;String, dynamic\u0026gt; json) { return MyModel( id: json[\u0026#39;id\u0026#39;] is String ? json[\u0026#39;id\u0026#39;] as String : \u0026#39;\u0026#39;, price: json[\u0026#39;price\u0026#39;] is num ? (json[\u0026#39;price\u0026#39;] as num).toDouble() : 0.0, createdAt: json[\u0026#39;createdAt\u0026#39;] is String ? DateTime.tryParse(json[\u0026#39;createdAt\u0026#39;] as String) ?? DateTime(1970, 1, 1) : DateTime(1970, 1, 1), enabled: json[\u0026#39;enabled\u0026#39;] is bool ? json[\u0026#39;enabled\u0026#39;] as bool : false, ); } Map\u0026lt;String, dynamic\u0026gt; toJson() { return { \u0026#39;id\u0026#39;: id, \u0026#39;price\u0026#39;: price, \u0026#39;createdAt\u0026#39;: createdAt.toIso8601String(), \u0026#39;enabled\u0026#39;: enabled, }; } @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is MyModel \u0026amp;\u0026amp; other.id == id \u0026amp;\u0026amp; other.price == price \u0026amp;\u0026amp; other.createdAt == createdAt \u0026amp;\u0026amp; other.enabled == enabled; } @override int get hashCode =\u0026gt; id.hashCode ^ price.hashCode ^ createdAt.hashCode ^ enabled.hashCode; } We have immutable properties ✅, a copy option ✅, a serialization/deserialization option ✅, equality comparison ✅, and additional constructors ✅. All of that in 66 lines of code!\nBut when we add a new property, we need to modify the class a lot and then the number grows quickly.\nA better approach is to rely on packages. So let’s see how that works!\nUse packages for model creation The major benefit of packages is that you don’t need to write all the boilerplate code. And in addition, you don’t risk making a silly mistake. With packages, you are on the safe side because they have proven their correctness in the past.\nAs a downside packages introduce dependencies. If you have many of them, there could be conflicts even if you update them regularly. In general, it should not be an issue but be aware that it can happen!\nequatable The equatable package adds equality checks to your class. You don’t need to overwrite hashCode and the == operator anymore.\nInstall the package, extend your class from Equatable, and override the props property. That’s it!\nEverything in props will be used to check for equality. You can also limit this to only the id if it makes sense in your case.\nHere is some code for you:\nclass MyEquatableModel extends Equatable { final String id; final double price; final DateTime createdAt; final bool enabled; const MyEquatableModel({ required this.id, required this.price, required this.createdAt, required this.enabled, }); @override List\u0026lt;Object?\u0026gt; get props =\u0026gt; [id, price, createdAt, enabled]; } equatable feature summary\nImmutable properties ❌ Copy option ❌ Serialization/deserialization ❌ Equality checks ✅ Additional constructors ❌ json_serializable To add serialization and deserialization capabilities, try out json_serializable.\nIt generates robust serialization and deserialization methods with many configuration options using the build runner.\nTo use it, install json_serializable and build_runner in your project. In addition, you need json_annotation for more configuration options.\nflutter pub add json_annotation dev:build_runner dev:json_serializable Have a look at this code snippet below to see how it works:\nimport \u0026#39;package:json_annotation/json_annotation.dart\u0026#39;; // where the generated code will be put part \u0026#39;my_serializable_model.g.dart\u0026#39;; @JsonSerializable() // required annotation class MySerializableModel { final String id; final double price; final DateTime createdAt; final bool enabled; const MySerializableModel({ required this.id, required this.price, required this.createdAt, required this.enabled, }); // connect to the generated deserialization method factory MySerializableModel.fromJson(Map\u0026lt;String, dynamic\u0026gt; json) =\u0026gt; _$MySerializableModelFromJson(json); // connect to the generated serialization method Map\u0026lt;String, dynamic\u0026gt; toJson() =\u0026gt; _$MySerializableModelToJson(this); } Execute the build runner with dart run build_runner build and wait. After that, your class has working fromJson and toJson methods.\nHere is the generated code:\n// GENERATED CODE - DO NOT MODIFY BY HAND part of \u0026#39;my_serializable_model.dart\u0026#39;; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** MySerializableModel _$MySerializableModelFromJson(Map\u0026lt;String, dynamic\u0026gt; json) =\u0026gt; MySerializableModel( id: json[\u0026#39;id\u0026#39;] as String, price: (json[\u0026#39;price\u0026#39;] as num).toDouble(), createdAt: DateTime.parse(json[\u0026#39;createdAt\u0026#39;] as String), enabled: json[\u0026#39;enabled\u0026#39;] as bool, ); Map\u0026lt;String, dynamic\u0026gt; _$MySerializableModelToJson( MySerializableModel instance, ) =\u0026gt; \u0026lt;String, dynamic\u0026gt;{ \u0026#39;id\u0026#39;: instance.id, \u0026#39;price\u0026#39;: instance.price, \u0026#39;createdAt\u0026#39;: instance.createdAt.toIso8601String(), \u0026#39;enabled\u0026#39;: instance.enabled, }; Your database name is probably not always identical to your property name. A good example is the createdAt property. If your database uses created_at you will get a runtime error. To fix this, add @JsonKey(name: \u0026quot;created_at\u0026quot;) above your property and rerun the build runner.\nYou can also have default values and checks included but this has some limitations. For example, you cannot set a default value for DateTime properties (at least not without additional code to convert it). While the package is quite mighty, I never really used it because of this important drawback. It doesn’t save you a lot of code that way.\nFor more configuration options check the package documentation!\njson_serializable feature summary\nImmutable properties ❌ Copy option ❌ Serialization/deserialization ✅ Equality checks ❌ Additional constructors ❌ freezed freezed is another package that helps reducing boilerplate code. You get immutable properties, a copy option, and equality check by default. To make it work, you need to install the following packages:\nflutter pub add freezed_annotation dev:build_runner dev:freezed Then, write your class like this:\nimport \u0026#39;package:freezed_annotation/freezed_annotation.dart\u0026#39;; // where the generated code will be put part \u0026#39;my_freezed_model.freezed.dart\u0026#39;; @freezed // required annotation abstract class MyFreezedModel with _$MyFreezedModel { // add mixin _$YourClass const factory MyFreezedModel({ required String id, required double price, required DateTime createdAt, required bool enabled, }) = _MyFreezedModel; // add _YourClass } The annotation is a bit more complex but together with the build runner you get a perfectly usable class!\nIn addition, freezed can work together with json_serializable. Modify your class and add the required code:\nimport \u0026#39;package:freezed_annotation/freezed_annotation.dart\u0026#39;; part \u0026#39;my_freezed_model.freezed.dart\u0026#39;; // additional part file part \u0026#39;my_freezed_model.g.dart\u0026#39;; @freezed abstract class MyFreezedModel with _$MyFreezedModel { const factory MyFreezedModel({ required String id, required double price, required DateTime createdAt, required bool enabled, }) = _MyFreezedModel; // factory, creates fromJson AND toJson factory MyFreezedModel.fromJson(Map\u0026lt;String, dynamic\u0026gt; json) =\u0026gt; _$MyFreezedModelFromJson(json); } ⚠️ Don’t forget to add the required packages for json_serializable!\nThe last thing that is missing on our list are additional constructors. You can add them manually to the class definition but there is no automated way to generate a special constructor for you.\nThe package documentation explains many more configuration options!\nfreezed feature summary\nImmutable properties ✅ Copy option ✅ Serialization/deserialization ❌ Equality checks ✅ Additional constructors ❌ Summary You can reduce boilerplate code by a lot when using packages. The more features you need and the more properties you have, the more they make sense.\nIf packages are not your way to go, then maybe AI can help you. Let’s see how AI can show us how to write a proper Dart data model.\nLet AI create the model for you Since AI came up, more and more developers use it in their daily work. I also came to the conclusion that it can save you a lot of time.\nEspecially for type-intensive tasks, AI make a huge productivity difference!\nCreating data models is such a task. Here is a prompt that returns pretty good results:\nPlaintext\nHelp me write a Dart data class according to my requirements. I will start by telling you how to create the class and in the end I will give you the properties that the class should have. General rules: - Don\u0026#39;t add any code comments - Just print the code, no explanations or anything else Requirements: - The class must be immutable, all fields must be final. - Create a named constructor .empty() that initializes all properties with a default value. - Create a copyWith method for the class. Use named parameters. - Create a factory fromJson constructor for deserialization. For every property check if the key exists in the JSON and if the value has the correct data type. If not, set a default value for primitive types. For nested types, assume there is a named constructor .empty(). Use this as a default value. - Create a toJson method. - Assume the property names are identical to the JSON keys. - Generate nested types according to the same rules. - Override hashCode and the == operator to provide equality checks. Assume instances are equal if all property values are equal. MyAiModel Properties: - String id - double price - DateTime createdAt - bool enabled - NestedObject data NestedObject properties: - List\u0026lt;String\u0026gt; tags - bool active Steal this prompt and adjust it to your needs. It will make your life a lot easier.\nAnother big benefit is that AIs can update your models easily. To do that just paste the model and tell the AI what properties you want to have added, removed, changed.\nAI feature summary\nImmutable properties ✅ Copy option ✅ Serialization/deserialization ✅ Equality checks ✅ Additional constructors ✅ Use Dart Quicktype Dart Quicktype is a web app that transforms JSON into Dart code. You can customize the output with various settings to get exactly the class you need. It understands nested objects, can rely on json_serializable for serialization, has support for equatable, and creates a copyWith method.\nDart Quicktype web app to convert JSON into Dart code\nThe downside is that you need to model your classes from JSON first. If you have an API result to convert, then it is a no-brainer.\nI use this tool a lot and especially for long classes since AI still tends to make some smaller mistakes.\nDart Quicktype feature summary\nImmutable properties ✅ Copy option ✅ Serialization/deserialization ✅ Equality checks ✅ Additional constructors ❌ Conclusion In this article we saw different ways how to write a proper Dart data model manually, with package support, with AI assistance, or by using online tools. At some point, the manual way just doesn’t make sense anymore and you might want to try out alternatives.\n","permalink":"https://quickcoder.org/posts/how-to-write-a-proper-dart-data-model/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere is how to write a proper Dart data model by hand, with various packages, and with AI assistance to make your life easier.\u003c/p\u003e\n\u003cp\u003eWriting good data models is essential for every developer. In this article I want to show you how to write a proper Dart data model manually, with package support, through AI assistance, or with a web tool. Take this as a starting point to write better models in the future.\u003c/p\u003e","title":"How To Write A Proper Dart Data Model (AI Prompt Included)"},{"content":"Introduction Since the release of Dart 3.7, your Flutter web app will give you warnings when you use dart:html in your code and have the latest Dart version activated. It was deprecated by the Flutter team. In this article, I am going to show you how to download files with the web package in Flutter apps. To do that, we will remove dart:html, replace it with the web package, and apply some code changes.\nHere is more information about what was deprecated and here is a migration guide about refactoring dart:html.\nThe use case You have a Flutter web app and want to give your users the ability to download text files like CSV that are created on the fly.\nLet’s assume, your app tracks the daily steps of a user. They can download monthly summaries with steps per day. This is a good example for a CSV file and can be easily used by other apps like Google Docs or Microsoft Office.\nHere is how to download files with the web package in Flutter apps and fix the warnings from the Dart analyzer.\nThe old way You probably have a dart:html import like this in your code.\nimport \u0026#39;dart:convert\u0026#39;; // ignore: avoid_web_libraries_in_flutter import \u0026#39;dart:html\u0026#39; as html; And you also have a download function to trigger a browser download like the one below. We assume here that csv already contains the prepared content.\nFuture _download(String csv, String fileName) async { final bytes = utf8.encode(csv); final blob = html.Blob([bytes]); final url = html.Url.createObjectUrlFromBlob(blob); final anchor = html.document.createElement(\u0026#39;a\u0026#39;) as html.AnchorElement ..href = url ..style.display = \u0026#39;none\u0026#39; ..download = \u0026#39;$fileName.csv\u0026#39;; html.document.body!.children.add(anchor); anchor.click(); html.document.body!.children.remove(anchor); html.Url.revokeObjectUrl(url); } This is what most AIs will propose to you at the moment when you ask them how such a download can be achieved. It still works but any future version could drop support for this piece of code.\nThe new way Instead of dart:html, the new way is to use the web package. The obvious first step is to install it with this command:\nflutter pub add web Then, we focus on the _download() function. Here is the updated version:\nFuture _download(String csv, String fileName) async { final blobParts = ([csv] as dynamic) as JSArray\u0026lt;BlobPart\u0026gt;; final blob = Blob(blobParts, BlobPropertyBag(type: \u0026#39;text/csv\u0026#39;)); final url = URL.createObjectURL(blob); final anchor = HTMLAnchorElement() ..href = url ..download = \u0026#39;$fileName.csv\u0026#39;; document.body?.append(anchor); anchor.click(); anchor.remove(); URL.revokeObjectURL(url); } We replaced all references to the old dart:html package. The call toJS is a little tricky and it took me a while to figure it out until the compiler stopped complaining.\nTo round things up, we update the import section as follows:\nimport \u0026#39;dart:js_interop\u0026#39;; import \u0026#39;package:web/web.dart\u0026#39;; With that, your function should work again and not produce any more deprecation warnings!\nConclusion With Dart 3.7, you need to make some changes to your code and move away from dart:html. In this article I showed you how to do that on a common example. Removing deprecated code parts is always recommended to make your code ready for the future.\n","permalink":"https://quickcoder.org/posts/how-to-download-files-with-the-web-package-in-flutter-apps/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eSince the release of \u003ca href=\"https://medium.com/dartlang/announcing-dart-3-7-bf864a1b195c\"\u003eDart 3.7\u003c/a\u003e, your Flutter web app will give you warnings when you use \u003ccode\u003edart:html\u003c/code\u003e in your code and have the latest Dart version activated. It was deprecated by the Flutter team. In this article, I am going to show you how to download files with the \u003ca href=\"https://pub.dev/packages/web\"\u003eweb\u003c/a\u003e package in Flutter apps. To do that, we will remove \u003ccode\u003edart:html\u003c/code\u003e, replace it with the \u003ca href=\"https://pub.dev/packages/web\"\u003eweb\u003c/a\u003e package, and apply some code changes.\u003c/p\u003e","title":"How To Download Files With The web Package In Flutter Apps"},{"content":"Introduction The Firebase Analytics Debug View is a handy tool to check what events your app sends in real time. However, it requires a bit of setup to work from a Flutter web app. Here is how to debug Firebase Analytics events from a Flutter web app.\nSetup The initial step is to connect your Flutter app with your Firebase project. Follow my guide in case you haven’t done this yet.\nTo work with Firebase Analytics, you then need to install the firebase_analytics and firebase_analytics_web (only for Flutter web apps) packages. In your code, get a reference to Firebase Analytics like this:\nfinal FirebaseAnalytics _analytics = FirebaseAnalytics.instance; After that, you can log events. Here are some examples:\n// user opens the app _analytics.logAppOpen(); // user clicks on a promotion element _analytics.logSelectPromotion(creativeName: productName); // user activates a product upgrade _analytics.logEvent(name: \u0026#34;product_activation\u0026#34;, parameters: {\u0026#34;license_key\u0026#34;: licenseKey}); Firebase Analytics offers predefined operations but when you have custom events, the generic logEvent() method is your friend.\nUsing the Debug View The Firebase Analytics Debug View is part of the Firebase Console. In your project go to Analytics -\u0026gt; Debug View to open it.\nFirebase Analytics Debug View waiting for debug events\nWhen you run your app directly from VS Code and trigger events, nothing will show up in the debug view. To make it work, you need to install the Google Analytics Debugger as a browser extension.\nLaunch your Flutter app on a web server with the command\nflutter run -d web-server The command shows an url to access the instance. Open it with your browser that has the Google Analytics Debugger installed and enabled.\n💡 Tip\nWhy do you need to launch a web server?\nWhen VS Code opens a browser window with your web app, it uses\na temporary profile which has no extensions installed. That\u0026rsquo;s\nwhy the events are not reported to the Firebase Analytics Debug View.\nTrigger some events to test it. It might take up to 15 seconds for the events to appear. Eventually, you should see something like this:\nFirebase Analytics Debug View with events reported from a Flutter web app\nAnd this is how to debug Firebase Analytics events from a Flutter web app.\nConclusion In this article I showed you how to debug Firebase Analytics events from a Flutter web app. It’s not as simple as when testing mobile apps but once you know how it works, it’s not a big deal anymore.\n","permalink":"https://quickcoder.org/posts/how-to-debug-firebase-analytics-events-from-a-flutter-web-app/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eThe Firebase Analytics Debug View is a handy tool to check what events your app sends in real time. However, it requires a bit of setup to work from a Flutter web app. Here is how to debug Firebase Analytics events from a Flutter web app.\u003c/p\u003e\n\u003ch1 id=\"setup\"\u003eSetup\u003c/h1\u003e\n\u003cp\u003eThe initial step is to connect your Flutter app with your Firebase project. \u003ca href=\"/posts/firebase-flutter-setup/\"\u003eFollow my guide\u003c/a\u003e in case you haven’t done this yet.\u003c/p\u003e","title":"How To Debug Firebase Analytics Events From A Flutter Web App"},{"content":"Introduction Sometimes testing is a joy and sometimes not. Here is a short excursion into the pitfalls of Flutter widget testing.\nI am currently developing a rather complex Flutter web app. Overall, it’s going great and I like the progress. But I wouldn’t write a short excursion into the pitfalls of Flutter widget testing if everything was perfect.\nThe project is rather complicated and the UI has a lot to offer. So I am investing heavily in testing it properly with widget tests.\nSadly, not everything is going great so far in that regard. I discover new problems with the tests on a daily basis. Here are some examples.\n🔔 Hint\nFun fact: I asked ChatGPT for help. The answers were useless.\nOverflow errors in tests A common issue that I have in my tests are overflow errors. We all know overflows, why they happen, and how we can fix them. But the main problem is that those errors only happen in the tests.\nWhen I run my app, there are no overflow errors. Everything is fine.\nAnd I couldn’t find really find a satisfying explanation for this.\nSince I am not the first one to run into that problem, there is a “solution”: Override the error handler and ignore everything related to overflow errors!\nHere is the StackOverflow discussion and this is the code:\nFlutterError.onError = _onError_ignoreOverflowErrors; Function _onError_ignoreOverflowErrors = ( FlutterErrorDetails details, { bool forceReport = false, }) { assert(details != null); assert(details.exception != null); // --- bool ifIsOverflowError = false; // Detect overflow error. var exception = details.exception; if (exception is FlutterError) ifIsOverflowError = !exception.diagnostics .any((e) =\u0026gt; e.value.toString().startsWith(\u0026#34;A RenderFlex overflowed by\u0026#34;)); // Ignore if is overflow error. if (ifIsOverflowError) print(\u0026#39;Overflow error.\u0026#39;); // Throw others errors. else FlutterError.dumpErrorToConsole(details, forceReport: forceReport); }; Be aware that you need to assign this function in every test. It’s not enough to assign it in a setUp() or setUpAll() function!\nSelectableText can be a pain Another pitfall that I didn’t know about is SelectableText.\nWhile this is usually not needed as much in mobile apps, I use it quite frequently in web apps so that users can copy stuff they need easily. But it’s not a good solution to just replace every Text widget with a SelectableText widget.\nLet’s see this example here:\nclass SimpleTextField1 extends StatelessWidget { const SimpleTextField1({super.key}); @override Widget build(BuildContext context) { return TextField( decoration: InputDecoration(label: Text(\u0026#34;my label\u0026#34;)), ); } } class SimpleTextField2 extends StatelessWidget { const SimpleTextField2({super.key}); @override Widget build(BuildContext context) { return TextField( decoration: InputDecoration(label: SelectableText(\u0026#34;my label\u0026#34;)), ); } } Here I have two classes that wrap a TextField. The only difference is that the label is a Text widget in the first class and a SelectableText in the second one.\nAnd here is the test code:\nvoid main() { testWidgets(\u0026#34;Testing\u0026#34;, (tester) async { final sut = MaterialApp(home: Scaffold(body: SimpleTextField1())); await tester.pumpWidget(sut); await tester.pumpAndSettle(); await tester.enterText(find.byType(TextField), \u0026#34;test\u0026#34;); expect(find.text(\u0026#34;test\u0026#34;), findsOneWidget); expect(find.text(\u0026#34;my label\u0026#34;), findsOneWidget); }); testWidgets(\u0026#34;Testing\u0026#34;, (tester) async { final sut = MaterialApp(home: Scaffold(body: SimpleTextField2())); await tester.pumpWidget(sut); await tester.pumpAndSettle(); await tester.enterText(find.byType(TextField), \u0026#34;test\u0026#34;); expect(find.text(\u0026#34;test\u0026#34;), findsOneWidget); expect(find.text(\u0026#34;my label\u0026#34;), findsOneWidget); }); } The first text works fine, but the second fails. The enterText method has a problem to find a single target to put the text in. For some reason SelectableText is also a valid target which leads to the “Too many elements” exception.\nScreenshot of an error in a widget test. The test tries to enter text in a TextField but fails because the TextField contains a label widget of SelectableText which causes the error to happen.\nSolution?!\nYou cannot fix the enterText method, so I just replaced the widget.\nAnd let’s be honest: Who copies the label of a text field?\nShouldn’t be a loss for the user.\nBe aware of buttons with icons Oh yes, the Finder might surprise you in a negative way.\nFor example when it comes to buttons.\nAll Material buttons have named constructors to quickly set an icon and a label (TextButton, FilledButton, ElevatedButton, OutlinedButton).\nBut try to find them by type in a widget test:\nTextButton.icon(...); find.byType(TextButton); // finds nothing The reason this fails is because the icon() constructors create a private type and not the actual button you expect. find.byType then of course fails because the types don’t match. It’s obvious once you know it. But this is part of the things you probably don’t expect in the first place.\nYou can however use find.byIcon() which will always work.\nConclusion This was a short excursion into the pitfalls of Flutter widget testing. In general it works well. But sometimes, it’s a frustrating topic. I hope this article gives you some hints to ship around some hurdles.\n","permalink":"https://quickcoder.org/posts/a-short-excursion-into-the-pitfalls-of-flutter-widget-testing/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eSometimes testing is a joy and sometimes not. Here is a short excursion into the pitfalls of Flutter widget testing.\u003c/p\u003e\n\u003cp\u003eI am currently developing a rather complex Flutter web app. Overall, it’s going great and I like the progress. But I wouldn’t write a short excursion into the pitfalls of Flutter widget testing if everything was perfect.\u003c/p\u003e\n\u003cp\u003eThe project is rather complicated and the UI has a lot to offer. So I am investing heavily in testing it properly with \u003ca href=\"/posts/flutter-widget-testing/\"\u003ewidget tests\u003c/a\u003e.\u003c/p\u003e","title":"A Short Excursion Into The Pitfalls Of Flutter Widget Testing"},{"content":"Introduction This article is about how to organize your widgets in Flutter apps in small, medium, and large apps. Not all of them work in every case and some of them require more attention than others. The results also depends on what you want have.\nA clear and well-organized structure A simple file structure As few files as possible As few imports as possible You cannot have everything but I can give you strategies to reach all those goals … but not at the same time.\nLet’s jump right in!\nThe “Chaos” option While only viable for really small projects, the Chaos option is probably the easiest one to follow. You simply put all widget files in the lib folder of your project.\nlib lib/main.dart lib/home_screen.dart lib/data_service.dart lib/header_bar.dart lib/footer_bar.dart lib/intro.dart lib/primary_button.dart lib/secondary_button.dart ... The benefit is clear: No folder structure to find the correct place for a file. Every file contains one widget. Simple and easy to understand.\nThis obviously only works for the smallest of projects because you will quickly end up with many files in your lib folder. The project tree in your IDE/code editor will be useless very fast and you have to rely on search features.\nFor beginners it’s a good choice. For team projects it’s not suited at all.\nThe “Feature” option If you follow the Clean Architecture approach, you have a folder structure based on features. The approach is then to add a shared folder to all feature folder that contains widgets that are used within that feature. This is what the Feature option is all about.\nFor widgets that are used across features, you would add a shared folder at the root level.\nlib lib/feature1 lib/feature1/data lib/feature1/data/... lib/feature1/domain/ lib/feature1/domain/... lib/feature1/presentation lib/feature1/presentation/some_screen.dart lib/feature1/presentation/... # A shared folder for widgets used in feature1 only lib/feature1/presentation/shared lib/feature1/presentation/shared/header_bar.dart lib/feature1/presentation/shared/... lib/feature2 lib/feature2/data lib/feature2/data/... lib/feature2/domain/ lib/feature2/domain/... lib/feature2/presentation lib/feature2/presentation/some_other_screen.dart lib/feature2/presentation/... # A shared folder for widgets used in feature2 only lib/feature2/presentation/shared lib/feature2/presentation/shared/footer_bar.dart lib/feature2/presentation/shared/... # A global shared folder for widgets used across features lib/shared lib/shared/content_container.dart lib/shared/... This structure is viable for all project sizes. It’s clear for everyone to what a widget belongs.\nThe downside is that you and/or your team need to stick to that structure. There are no easy ways to enforce this for Dart projects. When you work on feature3 and import a widget from feature2, it should be moved to the global shared folder. However, there are no built-in tools to help you with that.\nOther programming languages like C# offer reflection or packages like ArchUnitNET to create unit tests for that purpose. In Dart, this is not possible in an easy way. So it also depends on you and the team to keep this structure clean.\nFor small or hobby projects, the maintenance effort of this structure might be an overkill.\nThe “Private” option When you want to have fewer widget files, my recommendation is to use private classes inside your widgets. You can combine widgets with the same purpose in one file and make them private (except the parent one). With that approach you can reduce the number of classes by a lot.\nLet’s say you have the following widgets:\nmember_card.dart member_card_header.dart member_card_content.dart member_card_footer.dart They obviously belong together. So my advice would be to put MemberCardHeader, MemberCardContent, and MemberCardFooter in the member_card.dart file, make them private, and use them from inside.\nBut why private? 💡\nPrivate widgets can only be used in the same file. With that you can make sure that the widget is not used anywhere by accident. If it was a public one, you could import it in any file which renders your folder structure and namespaces useless in my opinion.\nBut there is a drawback: It only works for widgets that are not used anywhere else. As soon as you use a widget in two or more different widgets, this approach fails.\nYou might maybe have unit tests with type checks (expect(find.byType(MemberCardFooter), …)). These won’t work anymore. However, you can replace them with find.byKey. But this opens up the task of managing keys in your code…\nThe “Barrel file” option To tackle the number of import statements at the beginning of your Dart files, you can use barrel files. A barrel file contains many or all imports and exposes them again. This means that you only need to import the barrel file. Your import section will be much shorter with this approach.\nYou can do this manually or use one of many packages that help you with that like barrel_files, barreler, or barrel generator.\nMost IDEs and code editors support code folding. This feature lets you collapse a region of a code file so that you don’t need to scroll over it. Visual Studio Code supports this for Dart files.\nCode folding in Visual Studio Code for import statements\nI consider this a good alternative for barrel files with much less effort.\nYou can also configure Visual Studio Code to automatically collapse the import region by default. Go to File \u0026gt; Preferences \u0026gt; Settings \u0026gt; type “folding” in the search bar and active the option Folding Imports By Default.\nDefault import folding setting in Visual Studio Code\nIn my opinion code folding is a much better approach than the barrel files.\nThe “God file” option It’s like the Barrel option but on steroids. The benefits of the God File are that you can reduce your imports AND you can reduce the number of widget files.\nA God file (I came up with the name, it’s not a real thing) contains widgets so you only have to import this one file. By moving all widget classes to one file, you also reduce the number of files in your project by a lot.\nCreate a file widgets.dart and place all your reusable widgets inside. In your screen or page, you simply import this widgets.dart file and have access to all classes inside.\nObviously, the file will get large quickly. To battle this, you can split it up into buttons.dart, cards.dart, or whatever elements make sense in your specific app case. This is a compromise between large widget files and number of widget files in your project.\nAnother benefit is that imports are a no-brainer. You can’t mess it up here.\nConclusion In this article I gave you some tips on how to organize your widgets in a Flutter app. The outcome depends on what you want to achieve. Fewer widget files, an enforced folder structure, or just keep it simple? Pick your choices from these options and create your own rules!\n","permalink":"https://quickcoder.org/posts/how-to-organize-your-widgets-in-flutter-apps/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eThis article is about how to organize your widgets in Flutter apps in small, medium, and large apps. Not all of them work in every case and some of them require more attention than others. The results also depends on what you want have.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eA clear and well-organized structure\u003c/li\u003e\n\u003cli\u003eA simple file structure\u003c/li\u003e\n\u003cli\u003eAs few files as possible\u003c/li\u003e\n\u003cli\u003eAs few imports as possible\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eYou cannot have everything but I can give you strategies to reach all those goals … but not at the same time.\u003c/p\u003e","title":"How To Organize Your Widgets In Flutter Apps"},{"content":"Introduction To comply with the General Data Protection Regulation (GDPR) of the EU, the double opt-in process is the best way. It means that users need to confirm a registration with a secondary step. Usually, this is done by clicking a link in an email. In this article, I’ll show you how you can use Firebase to set up such a process. Let’s get started on how to implement double opt-in with Firebase and Flutter.\n❗ Warning\nI am not a lawyer and I cannot tell if your app/website violates the GDPR.\nThis article only shows how you could implement a double opt-in feature.\nPreparation We will use Firebase Authentication for the registration and verification process. Sadly, the double opt-in feature is not just a setting that we need to enable. Instead we need to write some code. But it is not very much ;-)\nIf you haven’t used Firebase Authentication before, read my instruction article to get started.\nI created a small Flutter web app to demonstrate everything. You can use whatever programming language you like since Firebase offers many SDKs to work with.\nWe start with a simple form to enter email and password to create an account. It can look like this:\nFlutter web app registration form to create an account with Firebase Authentication\nWhen you click on the button, the following code is executed:\nFuture\u0026lt;void\u0026gt; _register() async { try { final cred = await FirebaseAuth.instance .createUserWithEmailAndPassword(email: email, password: password); await cred.user!.sendEmailVerification(); if (mounted) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Center( child: Text(\u0026#34;Account created!\\\\r\\\\nCheck your inbox!\u0026#34;, textAlign: TextAlign.center)))); } setState(() {}); } catch (ex) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Center( child: Text(\u0026#34;The following error occured:\\\\r\\\\n$ex\u0026#34;, textAlign: TextAlign.center)))); } } } The createUserWithEmailAndPassword method should be self-explaining. Firebase even returns handy error messages in case the email is invalid or the password too weak.\nAfter that, we use sendEmailVerification to start the process. An email is sent to the user with a link to click. You can customize the email template in the Firebase Console.\nEmail template customization option in Firebase\nThe rest of the code is just to inform the user.\n💡 Tip\nWhile the registration hasn’t been confirmed, you could show a\nnotification in your app and prevent access to some features/sections.\nVerification When the user clicks the link in the email, the property emailVerified of the user object of Firebase Authentication is set to true.\nSadly, there is no mechanism to listen to that change because there is no method that fires in this case. As an alternative you can use the reload() function to get the most recent user data which includes the updated property emailVerified.\nHere is some example code of how you can do that:\nFuture\u0026lt;void\u0026gt; _checkIfVerified() async { try { await FirebaseAuth.instance.currentUser!.reload(); setState(() {}); } catch (ex) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Center( child: Text(\u0026#34;The following error occured:\\\\r\\\\n$ex\u0026#34;, textAlign: TextAlign.center)))); } } } In the demo application, the user needs to click a button to refresh the data. One could also use a timer for that but this solution is also not perfect. This is one of the weak points of the Firebase approach.\nFlutter web app to trigger verification of an account with Firebase Authentication\nThis is the entire magic. With very few lines of code and a bit of Firebase configuration, the email verification is in place.\nThe source code is available here.\nBonus You can also configure the verification to be handled by mobile apps by passing an ActionCodeSettings object to the sendVerificationEmail method. Here are more details for you about the process.\nCheck out the Flutter Firebase Compendium to learn more about Firebase and its services!\nConclusion You learned how to implement double opt-in with Firebase and Flutter in this article. It’s the best way to comply with data protection regulations. The setup is rather easy and if you don’t have hundreds of new users per day, you won’t pay anything with this system.\n","permalink":"https://quickcoder.org/posts/how-to-implement-double-opt-in-with-firebase-and-flutter/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eTo comply with the \u003ca href=\"https://en.wikipedia.org/wiki/General_Data_Protection_Regulation\"\u003eGeneral Data Protection Regulation (GDPR)\u003c/a\u003e of the EU, the double opt-in process is the best way. It means that users need to confirm a registration with a secondary step. Usually, this is done by clicking a link in an email. In this article, I’ll show you how you can use Firebase to set up such a process. Let’s get started on how to implement double opt-in with Firebase and Flutter.\u003c/p\u003e","title":"How To Implement Double Opt-In With Firebase and Flutter"},{"content":"Introduction One year ago I avoided code generation as best as I could. I always thought that it is way better to write the code yourself so that you know what happens. With generators, there is usually a different syntax with annotations and eventually some code pops out that doesn’t really look like what you have declared. But my view has changed. Now, I am always in favor of generators. And for all those struggling with it, I created this article. Learn how to create notifiers with the Riverpod generator in Flutter in the most simple way!\nSetup We need to install some packages first to use the Riverpod generator in our project.\nFirst, we need the flutter_riverpod package. It provides the general Riverpod features. Next, we need riverpod_annotation. These annotations will be used by the generator to create files on the fly. And we need the riverpod_generator package, of course. The last package is the build_runner package for Dart to execute the code generation.\nYou can even install build_runner and riverpod_generator as a dev dependency. Eventually, you can run the build runner with the command dart run build_runner build when you have created your notifiers. To not run it manually after every change, you can also start the build runner in watch mode with dart run build_runner watch.\nHere are 5 examples of how to create notifiers with the Riverpod generator in Flutter apps!\nExample 1: Notifier Here is a simple Notifier to start with. I use them a lot to store values like settings that I may need later. A perfect example is a filter setting. Once the user changes the filter, the result is updated based on the new setting.\nimport \u0026#39;package:riverpod_annotation/riverpod_annotation.dart\u0026#39;; part \u0026#39;my_notifier.g.dart\u0026#39;; @riverpod class MyNotifier extends _$MyNotifier { @override SomeClass build() { return SomeClass(number: 0, label: \u0026#34;Test\u0026#34;); } void update(SomeClass sc){ state = sc; } } class SomeClass { final int number; final String label; SomeClass({required this.number, required this.label}); } The generator needs 3 things:\nThe part import where you specify the file for the generator. The scheme is always the current file name with .g before the file extension.\nThe extends _$MyNotifier definition where the suffix is always the current class name prefixed by _$.\nThe build() method that specifies the type of the notifier (SomeClass in this example). When you read or watch this notifier, you will always get a SomeClass object.\nYour notifier can (and should) also include methods to modify the state. Just make sure that you always modify the state property in your method. Otherwise, a watch won’t be triggered and a read will not get the most recent value.\nExample 2: AsyncNotifier The big benefit of AsyncNotifier is that it has different states.\nThe loading state (AsyncLoading) is used to represent ongoing work\nThe error state (AsyncError) means something went wrong\nThe data state (AsyncData) contains the data provided by the notifier\nHere is an example:\nimport \u0026#39;dart:async\u0026#39;; import \u0026#39;package:riverpod_annotation/riverpod_annotation.dart\u0026#39;; part \u0026#39;my_async_notifier.g.dart\u0026#39;; @riverpod class MyAsyncNotifier extends _$MyAsyncNotifier { @override FutureOr\u0026lt;int\u0026gt; build() async { await Future.delayed(const Duration(seconds: 2)); return Future.value(3); } Future\u0026lt;void\u0026gt; doSomeAsyncStuff() async { state = const AsyncLoading(); await Future.delayed(const Duration(seconds: 2)); state = const AsyncData(3); } } I’ll briefly describe the differences to the previous code example.\nReturn type of build() method is wrapped in FutureOr build() method can be asynchronous The data type of the AsyncNotifier is AsyncValue\u0026lt;int\u0026gt;. When you read or watch the notifier, this is what you get. To handle the different states, you can use when() or whenData() if you don’t care about the other states. That’s it!\nWith these small changes, you can convert any Notifier to an AsyncNotifier. Here is how you react on changes:\n@override Widget build(BuildContext context) { final state = ref.watch(myAsyncNotifierProvider); return state.when( error: (error, stackTrace) =\u0026gt; const Text(\u0026#34;error\u0026#34;), loading: () =\u0026gt; const CircularProgressIndicator(), data: (data) =\u0026gt; Text(data.toString()), ); } AsyncNotifier is a powerful tool to make asynchronous UI updates really easy. With the different states you can always show the matching UI elements and not just put your users in front of a blank screen.\nExample 3: Provider I like to use Riverpod for dependency injection which makes testing easier. The ProviderScope widget has a property overrides for that. We only need to wrap all classes that we want to inject in providers. Here is how you do it.\nThis is the class to wrap. It’s a simple example of a database service that simulates reading and writing data.\nclass DatabaseService { Future\u0026lt;String\u0026gt; read() async { await Future.delayed(const Duration(seconds: 1)); return Future.value(\u0026#34;Test\u0026#34;); } Future\u0026lt;void\u0026gt; write(String data) async { await Future.delayed(const Duration(seconds: 1)); } } The provider for the generator looks like this:\nimport \u0026#39;package:riverpod_annotation/riverpod_annotation.dart\u0026#39;; import \u0026#39;package:test/database_service.dart\u0026#39;; part \u0026#39;database_service_provider.g.dart\u0026#39;; @riverpod DatabaseService databaseService(Ref ref) =\u0026gt; DatabaseService(); It’s a bit different to the notifiers from before. We start with the class name followed by the name of the generated provider (the suffix Provider will automatically be added, so the resulting provider is called databaseServiceProvider). Then it requires an argument of type Ref. You can pass this reference to your class or use it to read values from other providers that your class may require. The last step is the initialization of your DatabaseService class.\nTo use this provider, try await ref.read(databaseServiceProvider).read(). It does not make sense to watch this provider since the value will never change.\nAnd when you want to override this class for a test with a mocked class, then you can do this like that:\nvoid main() { runApp(ProviderScope( overrides: [databaseServiceProvider.overrideWithValue(DatabaseServiceMock())], child: const MyApp(), )); } The provider will then return the mock instead of the real class. This also works for notifiers.\nExample 4: No automatic disposal By default, the code generator always create types with AutoDisposeXXX. This means that the Riverpod framework can decide to throw them away and free resources if they are not part of the widget tree anymore. Sometimes, this behavior isn’t desired however. To fix that, change your declaration and set the keepAlive property to true. Here is a code example for you:\n// AutoDispose on @riverpod class MyNotifier extends _$MyNotifier { ... } // Method 1: AutoDispose off @Riverpod(keepAlive: true) // \u0026lt;-- watch out for the capital letter! class MyNotifier extends _$MyNotifier { ... } // Method 2: AutoDispose off @riverpod class MyNotifier extends _$MyNotifier { @override int build() { ref.keepAlive(); return 3; } } For example, you can use this to cache app settings like the theme, the font size, or anything else that makes sense in your case.\nExample 5: Notifier with AsyncValue vs AsyncNotifier I have found out to be misusing Notifiers in an async way. You can do this by setting the return type of the Notifier to AsyncValue\u0026lt;T\u0026gt;.\nLet’s compare a Notifier of type AsyncValue\u0026lt;int\u0026gt; and AsyncNotifier of type int. Here is the code for both of them:\n// AsyncNotifier import \u0026#39;dart:async\u0026#39;; import \u0026#39;package:riverpod_annotation/riverpod_annotation.dart\u0026#39;; part \u0026#39;my_async_notifier.g.dart\u0026#39;; @riverpod class MyAsyncNotifier extends _$MyAsyncNotifier { @override FutureOr\u0026lt;int\u0026gt; build() { return Future.value(3); } } // Notifier with AsyncValue import \u0026#39;package:riverpod_annotation/riverpod_annotation.dart\u0026#39;; part \u0026#39;my_other_async_notifier.g.dart\u0026#39;; @riverpod class MyOtherAsyncNotifier extends _$MyOtherAsyncNotifier { @override AsyncValue\u0026lt;int\u0026gt; build() { return const AsyncData(3); } } The original code differs in a few areas. The return types are not the same (FutureOr\u0026lt;int\u0026gt; and AsyncValue\u0026lt;int\u0026gt;) and also the code inside the build() method. Another thing that is worth mentioning but not in the code example here: the build() method of the Notifier cannot be async. If you need async initialization, go with an AsyncNotifier.\nThe generated code again looks pretty much identical for both files with very little differences except for the types\nAutoDisposeNotifierProvider\u0026lt;MyOtherAsyncNotifier, AsyncValue\u0026lt;int\u0026gt;\u0026gt; AutoDisposeAsyncNotifierProvider\u0026lt;MyAsyncNotifier, int\u0026gt; Notifier with return type AsyncValue on the left vs AsyncNotifier with return type int on the right. Can you spot the difference in the generated code?\nBut when you use them in your code, you might not even notice a difference at first. You can watch both with ref.watch() and they both offer the when() method to react to different states.\nBut there is one detail that shows that the AsyncNotifier is better in general. Here is the code to illustrate the fact:\n@override Widget build(BuildContext context) { final state1 = ref.watch(myAsyncNotifierProvider); final state2 = ref.watch(myOtherAsyncNotifierProvider); print(\u0026#34;AsyncNotifier: ${state1.runtimeType.toString()}\u0026#34;); print(\u0026#34;Notifier with AsyncValue: ${state2.runtimeType.toString()}\u0026#34;); ... } We watch both notifiers and print the types for comparison. Here is the output:\nOutput of types of AsyncNotifier and Notifier with AsyncValue.\nAsyncNotifier starts in the loading state and transitions to the data state when the build() method is completed. The Notifier with AsyncValue however is never in the loading state during initialization. This means when you watch those notifiers and want to display a loading indicator in the loading state during initialization, it won’t work with the Notifier because it never is in the loading state.\nApart from that, you will probably also have some cases where you replace when() with whenData() because you don’t really care about the other states. When I realized that I was at that point, it made sense to question the choice of using AsyncValue\u0026lt;T\u0026gt; as a return type. Eventually I just started using T.\nConclusion In this article, you learned how to create notifiers with the Riverpod generator package in Flutter apps so that you don’t need to write that much boilerplate code. With the provided examples you should be able to write your own notifiers with the help of code generation. It is difficult in the beginning but once you get the idea (and some details), you don’t want to go back anymore!\n","permalink":"https://quickcoder.org/posts/how-to-create-notifiers-with-the-riverpod-generator-in-flutter/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eOne year ago I avoided code generation as best as I could. I always thought that it is way better to write the code yourself so that you know what happens. With generators, there is usually a different syntax with annotations and eventually some code pops out that doesn’t really look like what you have declared. But my view has changed. Now, I am always in favor of generators. And for all those struggling with it, I created this article. Learn how to create notifiers with the Riverpod generator in Flutter in the most simple way!\u003c/p\u003e","title":"How To Create Notifiers With The Riverpod Generator In Flutter"},{"content":"Introduction Let’s say you have a notification icon and want to display the number of new messages. Or you have a shop with limited items that you want to advertise. Or you have a list and want to put the index number to every item. All those use cases can be accomplished with the Badge widget. All those simple solutions can be implemented within minutes. Here is how to use the Flutter Badge widget.\nNotification counter A notification counter is a typical UI element in many apps. It shows a number next to an icon. In this example I use a message icon so that the counter could indicate the number of new messages, for example. Here is what it could look like:\nA message icon with an indicator of new messages\nAnd this is the code that creates the result above.\nIconButton( icon: Badge.count(count: 25, child: Icon(Icons.message_rounded)), onPressed: () { goToMessageCenter(); }, ), There is an IconButton with its child icon wrapped inside a Badge widget. Since we only want to display a number, the Badge.count() constructor is a reasonable choice. It display all numbers between 1 and 999 but switches to 999+ when you pass a 4-digit number.\n💡 Tip\nIf you use negative numbers, it will display the entire input. There is no digit limit here!\nA nice and easy solution for a notification counter, isn’t it?\nShop item tag Your personal shop is going great and people are buying your products. To further encourage them to buy certain products, you want to display hints on the item cards. This could look something like this:\nA Badge widget on a shopping item indicating an upcoming scarcity\nHow can you to this with the Badge widget? Simple!\nSizedBox( height: 200, width: 200, child: Card( child: Padding( padding: const EdgeInsets.all(8.0), child: Badge( offset: Offset(-60, 0), label: Text(\u0026#34;only few left\u0026#34;), child: Column( crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.start, children: [ Spacer(), Center(child: Icon(Icons.computer_rounded)), Spacer(), Text(\u0026#34;Mac Book Pro M3 Max\u0026#34;), Text(\u0026#34;\\\\$3599\u0026#34;) ]))))) The Badge widget is the parent of the content. Usually it would appear in the top right corner and extend to the right. However, you can correct the position with the Offset property to put it inside the card’s boundaries. The label property accepts any widget, but apart from Text widgets there aren’t many that really make sense here.\nYour sales will go through the roof with such a simple adjustment!\nList index number For my current project I needed an item index for list elements in my UI. Maybe the Badge widget is not the first thing that comes to your mind in this case. But it works with very little effort. Here is the result:\nA list of item with a number badge in the bottom right corner. This is also the result of the Badge widget.\nAnd the relevant code part is this one here:\nSizedBox( width: 200, height: 140, child: Badge.count( alignment: Alignment.bottomRight, padding: EdgeInsets.zero, offset: Offset(-10, -24), count: widget.index + 1, child: Card( child: Padding( padding: const EdgeInsets.symmetric(horizontal: gapMedium), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Tooltip( message: \u0026#34;Break Time\u0026#34; child: Text(\u0026#34;Break Time\u0026#34;, style: Theme.of(context) .textTheme .headlineSmall, maxLines: 1, overflow: TextOverflow.ellipsis), ), Text(\u0026#34;05:00\u0026#34;, style: Theme.of(context).textTheme.displaySmall) ]), )) ))); Again, I am using the count() constructor to show a number. Together with the alignment, padding, and offset property, I can place the badge in the exact location where I want it to have.\nShort, simple, and functional!\nConclusion In this article you learned how to use the Flutter Badge widget in different use cases. While you can achieve the same results with other widgets, it’s essential to know the Flutter framework and the tools it offers.\n","permalink":"https://quickcoder.org/posts/how-to-use-the-flutter-badge-widget/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eLet’s say you have a notification icon and want to display the number of new messages. Or you have a shop with limited items that you want to advertise. Or you have a list and want to put the index number to every item. All those use cases can be accomplished with the \u003ca href=\"https://api.flutter.dev/flutter/material/Badge-class.html\"\u003eBadge\u003c/a\u003e widget. All those simple solutions can be implemented within minutes. Here is how to use the Flutter Badge widget.\u003c/p\u003e","title":"How To Use The Flutter Badge Widget"},{"content":"Introduction For your Instagram clone or your member app: Here is how to make rounded profile pictures in Flutter. An easy and fast solution for all app types!\nIn this article, I am going to show you how to make rounded profile pictures in Flutter apps. Why? Because it’s state of the art on many well-known websites. Even Medium uses them, just take a look here:\nScreenshot of the Medium homepage with rounded author profile pictures\nI will show you two approaches on how you can achieve that in this article.\nCircleAvatar widget The CircleAvatar widget does the entire job for you. Just pass your image and you are good to go.\nCircleAvatar( backgroundImage: NetworkImage(\u0026#34;someUrl\u0026#34;), ) Or if you want to use an image from your app, use this code:\nCircleAvatar( backgroundImage: AssetImage(\u0026#34;path/to/asset.jpg\u0026#34;), ) But CircleAvatar is not only for images, you can pass any child widget you want. A good example is showing the initials of a user when there is no profile picture.\nCircleAvatar( backgroundColor: Theme.of(context).primaryColor, child: Text( \u0026#34;AD\u0026#34;, style: const TextStyle( fontSize: 24, fontWeight: FontWeight.w700, fontFamily: \u0026#34;Arial Black\u0026#34;), textAlign: TextAlign.center, ), ); This is what it can look like:\nA fallback example of a rounded profile picture with CircleAvatar when no image is defined\nOf course, you can define sizes, colors, set foreground and/or background images, and you can even decide what should happen in case the image loading fails.\nDo it yourself In case you don\u0026rsquo;t want to use CircleAvatar, you can achieve the same with a bunch of other widgets. Look at this small code example:\n@override Widget build(BuildContext context) { return Center( child: SizedBox( width: 256, height: 256, child: ClipOval( child: Image.asset( \u0026#34;resources/images/loc2.jpg\u0026#34;, fit: BoxFit.cover, )))); } The ClipOval widget will crop your image the way you want it. If you don’t want a circle, you can play around with the width and height properties. Also consider using a different fit setting in case you want your image to show only a portion of the entire image.\nA not perfectly square image cropped to a square circle\nMy example image is slightly higher than wider. That’s why there is a flat spot at the top of the circle. When you want a nicely-shaped circle, you need an image with identical width and height.\nHere is another code example:\nWidget _buildImageButtonWithFallback(String base64Image) { final bytes = base64Decode(base64Image); return Container( width: 70, height: 70, decoration: const ShapeDecoration( shape: CircleBorder(side: BorderSide(color: Colors.black, width: 1))), child: ClipOval( child: Image.memory(bytes, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) =\u0026gt; const Icon(Icons.person)), )); } In this approach, we even have a CircleBorder around the picture and the image is loaded from a base64 string. When you want to go really fancy, use FadeInImage with a placeholder while the actual image loads. This works for image resources and network downloads.\nAnd if you are not into circle shapes, try ClipRect to crop your image in a rectangular or square shape.\nConclusion In this article, we learned how to make rounded profile pictures in Flutter apps. In addition, I gave you further tips on how to work with images and cropping them.\n","permalink":"https://quickcoder.org/posts/how-to-make-rounded-profile-pictures-in-flutter/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eFor your Instagram clone or your member app: Here is how to make rounded profile pictures in Flutter. An easy and fast solution for all app types!\u003c/p\u003e\n\u003cp\u003eIn this article, I am going to show you how to make rounded profile pictures in Flutter apps. Why? Because it’s state of the art on many well-known websites. Even Medium uses them, just take a look here:\u003c/p\u003e\n\u003cfigure\u003e\n\u003cp\u003e\u003cimg alt=\"Screenshot of the Medium homepage with rounded author profile pictures\" loading=\"lazy\" src=\"/posts/how-to-make-rounded-profile-pictures-in-flutter/images/medium_rounded_profile_pictures.webp\"\u003e\u003c/p\u003e","title":"How To Make Rounded Profile Pictures In Flutter"},{"content":"Introduction Get rid of annoying bugs and learn how to prevent a double click on buttons in Flutter apps with this easy approach. Your users will also thank you for this!\nWe always assume the best. All people are treated equally, nobody wants war, and users only click once on a button and wait patiently until something happens. Sadly, I can’t fix everything but for the last point, I have a tip for you. Here is how to prevent a double click on buttons in Flutter apps.\nIdea My simple approach disables the button after the click and enables it again once the operation is completed.\nYou can drive this even further by changing the button caption or displaying a progress indicator. It’s always a good idea to show users that something is happening in your app. If nothing happens they are likely to click a button or go back to the parent page which might cause problems.\nSo let’s see how to prevent a double click on buttons in Flutter apps by disabling them while work is in progress.\nCode The first step is to create a button widget. It’s just a simple wrapper so that we don’t need to write boilerplate code all over the entire app. In my case, I just wrap an ElevatedButton to get started.\nimport \u0026#39;package:flutter/material.dart\u0026#39;; class ButtonWidget extends StatefulWidget { final String caption; final Function() onPressed; const ButtonWidget({Key? key, required this.caption, required this.onPressed}) : super(key: key); @override State\u0026lt;ButtonWidget\u0026gt; createState() =\u0026gt; _ButtonWidgetState(); } As you can see, I pass in the caption and the function that is executed when the button is pressed. You might also have noticed that the button is a StatefulWidget. The reason for this comes now.\nclass _ButtonWidgetState extends State\u0026lt;ButtonWidget\u0026gt; { bool _isRunning = false; @override Widget build(BuildContext context) { return ElevatedButton( onPressed: _isRunning ? null : () async =\u0026gt; await _onPressed(), child: Text(widget.caption)); } Future _onPressed() async { setState(() { _isRunning = true; }); try { await widget.onPressed(); } finally { setState(() { _isRunning = false; }); } } } Look at the _onPressed method. First, a variable is set to true that indicates that the app is doing something. While this variable is true, the button has null assigned as the onPressed handler. This disables the button and you cannot click it anymore. Then, we wait for the actual operation to complete and set our state variable to false again.\nThe try finally block ensures that the button won\u0026rsquo;t be stuck in the pressed state even if an error happens.\nPretty easy solution!\nOf course, you can enhance it by changing the button or the colors when the button is disabled to make it even clearer. A progress indicator could also be an option. Here is a great package that offers many different animations that you can use!\nConclusion In this article, you learned how to prevent a double click on buttons in Flutter apps. By disabling the button after the first click and re-enabling it once the action is completed, we can make sure that the click is only handled once.\n","permalink":"https://quickcoder.org/posts/how-to-prevent-a-double-click-on-buttons-in-flutter-apps/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eGet rid of annoying bugs and learn how to prevent a double click on buttons in Flutter apps with this easy approach. Your users will also thank you for this!\u003c/p\u003e\n\u003cp\u003eWe always assume the best. All people are treated equally, nobody wants war, and users only click once on a button and wait patiently until something happens. Sadly, I can’t fix everything but for the last point, I have a tip for you. Here is how to prevent a double click on buttons in Flutter apps.\u003c/p\u003e","title":"How To Prevent A Double Click On Buttons In Flutter Apps"},{"content":"Introduction Learn why your apps crash and why you get complaints from users. Here is how to monitor your mobile apps with Firebase Crashlytics. Firebase Crashlytics is a light-weight solution to get insight about why your users get app crashes and exception messages. It is part of the Firebase cloud services, but can be used for free and supports mobile apps.\nA dashboard shows you in detail your app metrics and allows inspecting reported crashes in detail. You get information about the exception, the exact line of code, and also the current stack trace at that time.\nScreenshot of the dashboard of Firebase Crashlytics\nHere is a list of information that the dashboard provides\nCrash trends\nList of grouped issues\nException type and message, line of code, stack trace, log messages\nDevice types, operating systems, timestamps, app version\nThe setup will take 10 minutes, so let’s get started right away and learn how to monitor your mobile apps with Firebase Crashlytics!\nFirebase setup The first steps are to create a Firebase account and to connect it with your Flutter app. I’ve published a detailed guide about this process. You can read it here.\nCreate a Firebase project\nUse the flutterfire CLI and firebase CLI to connect your Flutter app with Firebase\nThat’s it!\nThe flutterfire CLI creates the Firebase projects for your Android and iOS applications for you. You don’t have to do anything.\nYou can find your Crashlytics Dashboard in Firebase in the Run section. Currently, it’s empty. We will change that in the next steps.\nScreenshot of the Firebase navigation menu with the Crashlytics entry in the Run section.\nLet’s move on to the app code!\nApp setup In this example, I will show you how to add Firebase Crashlytics to a Flutter app. If you are using a different programming language, check out the documentation. However, the steps are pretty similar.\nInstall the required packages in your app:\nfirebase_core firebase_crashlytics Not sure how this works? Read this article!\nInitialize Firebase in your main() method and connect Firebase Crashlytics with the default exception handlers of Flutter. Every time an unhandled exception is thrown, a error report will be uploaded to Firebase. Here is the code:\nvoid main() async { WidgetsFlutterBinding.ensureInitialized(); // connect to your Firebase project await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); // handle unhandled exceptions of the Flutter framework FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError; // handle all unhandled exception even outside the Flutter framework (sync and async) PlatformDispatcher.instance.onError = (dynamic error, StackTrace? stackTrace) { FirebaseCrashlytics.instance.recordFlutterFatalError( FlutterErrorDetails(exception: error, stack: stackTrace)); return true; }; runApp(const MyApp()); } By default, crash reports are uploaded automatically. You can disable this behavior with the following command:\nFirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(false); It is recommended that you ask your users first if they agree that crash information is collected by a 3rd party system.\nUpload iOS debug symbols While Android apps work out of the box, iOS apps require an additional setup step. Firebase requires the debug symbols of your app to be uploaded, so that you can see report details. This is how your dashboard looks like when the symbols are missing:\nScreenshot of the Firebase Crashlytics Dashboard with missing debug information for iOS\nBy default, the flutterfire configure command should add this automatic upload. In case it doesn’t work or you make the connection with flutterfire, here are the steps to add the automation to your iOS project:\nIn your XCode project, go to Build Phases, and create a New Run Script Phase with the + button in the top left corner. Then, paste the following code:\nPATH=${PATH}:$FLUTTER_ROOT/bin:$HOME/.pub-cache/bin flutterfire upload-crashlytics-symbols --upload-symbols-script-path=$PODS_ROOT/FirebaseCrashlytics/upload-symbols --platform=ios --apple-project-path=${SRCROOT} --env-platform-name=${PLATFORM_NAME} --env-configuration=${CONFIGURATION} --env-project-dir=${PROJECT_DIR} --env-built-products-dir=${BUILT_PRODUCTS_DIR} --env-dwarf-dsym-folder-path=${DWARF_DSYM_FOLDER_PATH} --env-dwarf-dsym-file-name=${DWARF_DSYM_FILE_NAME} --env-infoplist-path=${INFOPLIST_PATH} --default-config=default Add an additional build phase in XCode to automatically upload iOS debug symbols to Firebase Crashlytics\nThis will be executed after every iOS build and Firebase always receives the current debug symbols. From now on, the dashboard will show all details of any future crashes.\nYou can also just rerun the flutterfire configure command to solve this issue.\nReport errors and crashes You can either use the global exception handlers to catch and upload crash information to Firebase Crashlytics or you can trigger an error report yourself.\nFirebaseCrashlytics.instance.recordError(Exception(\u0026#34;Test\u0026#34;), StackTrace.current); Reports are sent to Firebase in regular intervals if data transmission is enabled. Sometimes, it can take a few minutes until the dashboard is updated. Here is an example dashboard with a recorded exception in an iOS app:\nScreenshot of the Firebase Crashlytics dashboard with recent crash information of an iOS app.\nLogging Firebase Crashlytics has a logging feature so that you can enhance your error reports with more information. Logs are collected until the next transmission to Firebase and appended to the report.\nTo create a log entry, use the following code:\nFirebaseCrashlytics.instance.log(\u0026#34;This is a log message\u0026#34;); throw Exception(\u0026#34;this is another test with logs\u0026#34;);\u0026lt;br\u0026gt; When you click on an issue in your dashboard, you get more detailed information including the logs. Here is how it looks:\nScreenshot of an issue with attached log messages in Firebase Crashlytics\nYou cannot use the logs for local debugging since they are only visible alongside a report in Firebase. If you need logging for any other purpose, use a dedicated logging framework instead.\nConclusion In this article, you learned how to monitor your mobile apps with Firebase Crashlytics. It’s an easy solution for iOS and Android to know why your apps crash. These insights are helpful to increase user satisfaction and decrease negative reviews for any app in production use.\n","permalink":"https://quickcoder.org/posts/how-to-monitor-your-mobile-apps-with-firebase-crashlytics/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eLearn why your apps crash and why you get complaints from users. Here is how to monitor your mobile apps with Firebase Crashlytics. \u003ca href=\"https://firebase.google.com/products/crashlytics/\"\u003eFirebase Crashlytics\u003c/a\u003e is a light-weight solution to get insight about why your users get app crashes and exception messages. It is part of the \u003ca href=\"https://firebase.google.com/\"\u003eFirebase cloud services\u003c/a\u003e, but can be used for free and supports mobile apps.\u003c/p\u003e\n\u003cp\u003eA dashboard shows you in detail your app metrics and allows inspecting reported crashes in detail. You get information about the exception, the exact line of code, and also the current stack trace at that time.\u003c/p\u003e","title":"How To Monitor Your Mobile Apps With Firebase Crashlytics"},{"content":"Introduction Here is how to set up a free repository for your Flutter packages with Cloudsmith in no time!\nA package repository has many benefits over packages in local folders or GitHub repositories. If you want to publish your package publicly, you usually are fine with pub.dev. In case you need a private solution, continue reading here. I’ll show you how to set up a free repository for your Flutter packages with Cloudsmith.\nCloudsmith is free as long as you have only one developer. For bigger teams you need to upgrade your plan and pay depending on your team size. On the other hand you also get package repositories for other programming languages like Java (Maven) or C# (Nuget) and tools like Chocolatey or Docker.\nThe Cloudsmith package repository main page\nWhy not just use GitHub? Fair question!\nYou can reference GitHub branches, tags, or commit-ids in your pubspec.yaml. So wouldn’t it be enough to just use GitHub?\nThe downside is that you can’t specify versions when using GitHub references. So let’s say you reference a branch with your app and everything works today, but tomorrow your build breaks because the branch was updated.\nAnother problem is that you cannot have transitive dependencies with other packages hosted on GitHub. I talked about this in detail here as I struggled with it just recently.\nIf those reasons are not a issue for you, GitHub can already be enough for your needs. And if you use it already for code hosting, then it’s a no-brainer.\nIn all other cases keep on reading to learn how to set up a free repository for your Flutter packages with Cloudsmith. I’ll talk you through all details in the next chapters!\nCreate account The first step is to create a free account. Cloudsmith also offers paid plans for larger teams with more features but we will focus on the basics here.\nGo to https://cloudsmith.com and click on the button Start your free trial in the top right corner.\nCreate a new account on the Cloudsmith homepage\nEnter your details, create a new account, and confirm it with the email that Cloudsmith sends you.\nAfter that, you will be prompted to join an existing organization or to create a new one. It’s just another hierarchical layer on top of repositories. Be aware that the organization name will be part of the URL when uploading or consuming packages.\nCreate a new organization or join an existing one after creating an account for Cloudsmith\nAs a bonus you can try out all paid features for 14 days. After that you need to switch another plan to benefit from those. If you don’t do anything, your available feature set will be reduce to the free Core plan.\nNow, let’s move on to creating a new repository in our organization!\nCreate repository A repository is just a container for all your packages. You are not limited to one certain type of package per repository. It’s totally fine and possible to use Dart packages, npm packages, and Nuget packages in the same repository from a technical point of view. If this makes sense, is up to you however.\nAfter creating your organization, you will be asked to create a repository.\nChoose a descriptive repository name as an identifier. It will be part of the URL but can be changed later. The URL will stay the same after the initial creation.\nThe storage region might be relevant for data protection. If you want to use the service for free, you have to stick with the default region.\nWith the repository type you define the access level and the feature set. A private repository has the least features available for free while open-source repositories have more benefits.\nConfigure your repository parameters like name, storage region, and type for your Cloudsmith account\nAfter you created your repository, the main repository page appears.\nThe next step is to upload packages so that your apps can consume them.\nUpload packages Cloudsmith offers detailed instructions for every supported package and repository format. Since we are focusing on a Dart/Flutter repository, I’ll show you the required steps for that.\nChoose the Dart package format in the lower tab bar to see the instruction panel.\nYou have 4 options to upload packages:\nmanually with the web UI with the native Dart/Flutter tools with the Cloudsmith CLI tools or with the Cloudsmith API The web UI is useful for upload testing for the first time or configuring the repository. You can do any automations with it. For that, you should either use the native Dart/Flutter tooling or the Cloudsmith CLI. The main difference is that the Cloudsmith CLI allows upload of 5GB or more while the native tooling is limited to 200MB per file. The Cloudsmith API is useful when you want to create custom apps that interact with your repository.\nNo matter what approach you choose, the instruction panel shows everything you need to know to get access.\nCloudsmith instructions to upload packages\nThe final step is to learn how we can consume packages.\nConsume packages The setup is completed and you have pushed your first packages to the repository. Now, how do you consume those?\nIn your repository, click on the blue button Set Me Up in the top-right corner and choose the Dart package format.\nThe instructions will guide you through the steps necessary for your apps to connect with the repository.\nFirst, add a package reference to your pubspec.yaml\ndart pub add your-package:1.2.3 --hosted-url \u0026lt;https://dart.cloudsmith.io/\u0026gt;\u0026lt;your-orga\u0026gt;/\u0026lt;your-repo\u0026gt;/ Next, add an authentication token\necho \u0026#39;\u0026lt;your-token\u0026gt;\u0026#39; | dart pub token add \u0026lt;https://dart.cloudsmith.io/\u0026gt;\u0026lt;your-orga\u0026gt;/\u0026lt;your-repo\u0026gt;/ Enter secret token: Requests to \u0026#34;\u0026lt;https://dart.cloudsmith.io/\u0026gt;\u0026lt;your-orga\u0026gt;/\u0026lt;your-repo\u0026gt;/\u0026#34; will now be authenticated using the secret token. Run flutter pub get and test if errors occur. If not, your setup was successful!\nDetailed description of how to connect with the Cloudsmith Dart repository and install packages in a Dart/Flutter app\nThis concludes the basic steps of how to set up a free repository for your Flutter packages.\nBonus: GitHub Actions pipeline As software developer, we tend to automate things as much as possible.\nI manage the code of my packages in GitHub and I also have GitHub Action pipelines to build, test, and deploy them. Here is an example of such a pipeline:\nname: Release on: workflow_dispatch: branches: - master env: PACKAGE_REPOSITORY_TOKEN: ${{ secrets.PACKAGE_REPOSITORY_TOKEN }} jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Flutter uses: subosito/flutter-action@v2 with: flutter-version: \u0026#39;3.22.0\u0026#39; channel: \u0026#39;stable\u0026#39; - name: Add package repository token run: dart pub token add \u0026lt;https://dart.cloudsmith.io/\u0026gt;\u0026lt;your-repository\u0026gt;/dart/ --env-var PACKAGE_REPOSITORY_TOKEN - name: Install dependencies run: flutter pub get - name: Run unit tests run: flutter test - name: Publish package run: flutter pub publish --force - name: Create release branch run: | VERSION=$(awk \u0026#39;/version:/{print $2; exit}\u0026#39; pubspec.yaml) git checkout -b release/$VERSION git push --set-upstream origin release/$VERSION Ok, what does it do?\nYou’ll notice the trigger workflow_dispatch which means you can only trigger this pipeline manually on the master branch. I am hesitant to remove this step because then a faulty commit could lead to all consuming apps breaking immediately.\nNext, Flutter is set up with a specific version. You could extract the version into a variable but I don’t see any benefits.\nThe following step sets up the connection to our repository at CloudSmith. The token is stored as a secret (secrets.PACKAGE_REPOSITORY_TOKEN) in GitHub so that it doesn’t appear in log files or the code base.\nThen, the usual stuff happens. Install dependencies, run tests, and publish the package eventually. Make sure to connect to your package repository before running flutter pub get. And in case you want to test publishing, use the argument --dry-run or -n on the flutter pub publish command.\nThe last step creates a release branch with the version number stored in pubspec.yaml and pushes the code there. Every release branch corresponds to a specific package version.\nCloudsmith package versions on the left and the corresponding release branches of the GitHub repository on the right.\nI wanted to give you some inspiration of what is possible. Use this pipeline as a baseline and enhance it to suit your needs.\nConclusion In this article you learned how to set up a free repository for your Flutter packages with Cloudsmith. When you are a single developer and just need a private repository for your packages, Cloudsmith can be a solution. It’s fairly easy to manage and apart from the initial setup, you probably rarely need to visit their homepage.\n","permalink":"https://quickcoder.org/posts/how-to-set-up-a-free-repository-for-your-flutter-packages/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere is how to set up a free repository for your Flutter packages with Cloudsmith in no time!\u003c/p\u003e\n\u003cp\u003eA package repository has \u003ca href=\"https://long.sweet.pub/the-struggle-with-flutter-packages-in-private-github-repositories-50e53852e4aa\"\u003emany benefits over packages in local folders or GitHub repositories\u003c/a\u003e. If you want to publish your package publicly, you usually are fine with pub.dev. In case you need a private solution, continue reading here. I’ll show you how to set up a free repository for your Flutter packages with Cloudsmith.\u003c/p\u003e","title":"How To Set Up A Free Repository For Your Flutter Packages"},{"content":"Introduction Built-In vs external package: Here are two essential methods of how you can find out if two Flutter objects are equal or not.\nEquality comparison is essential in any kind of app. Duplicate objects are often undesired. In this article I’ll demonstrate how you can find out if two Flutter objects are equal or not. You might probably know the drill from other programming languages and Dart makes no exception here. The article will show you two ways of comparison. A longer one with built-in methods provided by Dart and another one that relies on an external package but is less verbose.\nCompare with built-in methods Let’s assume we have a simple class with an identifier, a name, and an email property. When using the options provided by the Dart language, you would override two methods:\nThe equality operator == And the method hashCode Here is a code example:\nclass Person { final int id; final String name; final String email; Person(this.id, this.name, this.email); // Overriding the equality operator @override bool operator ==(Object other) { if (identical(this, other)) return true; if (other is! Person) return false; return id == other.id \u0026amp;\u0026amp; name == other.name \u0026amp;\u0026amp; email == other.email; } // Overriding the hashCode method @override int get hashCode =\u0026gt; id.hashCode ^ name.hashCode ^ email.hashCode; } Ok, let’s break down what happens in this code.\nFirst, the operator == is overridden. The code uses the identical function to determine if the two objects share the same reference. If not, then a type check is performed. In case the types match, all property values are compared.\nThe second step is to override hashCode. This ensures that objects with the same property values are considered equal because they return the same hash value. It’s also useful for efficient lookups in hash-based collections like HashMap or HashSet.\nWhile this is straight forward, it can get quite verbose when you have more properties. The second approach solves this problem for you.\nCompare with the equatable package The package equatable will do everything for you that I described above and save you many lines of code. The only things you need to make your object derive from Equatable and specify the properties you want to include in the comparison.\nHere is an example:\nimport \u0026#39;package:equatable/equatable.dart\u0026#39;; class Person extends Equatable { final int id; final String name; final String email; Person(this.id, this.name, this.email); @override List\u0026lt;Object?\u0026gt; get props =\u0026gt; [id, name, email]; } The props getter returns all properties that should be taken into consideration when comparing objects.\nSometimes, your objects have unique identifiers from a database. In this case it would be enough to just compare the id property and skip the others.\nThis approach requires way less lines of code to achieve the same result. But you have the downside of relying on (another) 3rd party package.\nBonus: Generate data classes quickly Dart Quicktype is a great service that converts JSON into clean Dart objects. It has many configuration options to make your class exactly how you need it. There is also an equatable option which I highly recommend.\nJSON to Dart object conversion with Dart Quicktype\nOf course, your favorite AI model can probably do the same for you but in my experience there is often manual editing required. I use this service instead whenever I can.\nConclusion In this article I showed you two ways how you can find out if two Flutter objects are equal or not. You can do it yourself to not rely on another 3rd party package. Or you can just the equatable package and save many lines of code. Both ways will work fine.\n","permalink":"https://quickcoder.org/posts/how-you-can-find-out-if-two-flutter-objects-are-equal-or-not/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eBuilt-In vs external package: Here are two essential methods of how you can find out if two Flutter objects are equal or not.\u003c/p\u003e\n\u003cp\u003eEquality comparison is essential in any kind of app. Duplicate objects are often undesired. In this article I’ll demonstrate how you can find out if two Flutter objects are equal or not. You might probably know the drill from other programming languages and Dart makes no exception here. The article will show you two ways of comparison. A longer one with built-in methods provided by Dart and another one that relies on an external package but is less verbose.\u003c/p\u003e","title":"How You Can Find Out If Two Flutter Objects Are Equal Or Not"},{"content":"Introduction Let’s welcome Bitwarden, KeePass, LastPass, and others. Here is how to add password manager support to your Flutter apps. In addition, you can use other autofill options for credit cards, birthdays, address data, and more in the same way!\nPassword managers save us time and help choosing stronger and more diverse passwords in general. Instead of remembering hundreds of credentials for all your sites, a master password is enough. In this article I will show you how to add password manager support to your Flutter apps. In addition, you also can configure this to support autofills for emails, username, birthdays, address data, or credit card information!\nLogin example Imagine we have a simple login page with two TextField widgets for the username and a password.\nTo support password managers and autofill options in general, we simply wrap our input fields with an AutofillGroup widget. Then, we specify the AutofillHints per field. There are many options to choose from and use can define them in an array.\nHere is the code example for you:\nclass LoginScreen extends StatelessWidget { final _usernameController = TextEditingController(); final _passwordController = TextEditingController(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(\u0026#39;Login\u0026#39;)), body: Padding( padding: const EdgeInsets.all(16.0), child: AutofillGroup( child: Column( children: [ TextField( controller: _usernameController, decoration: InputDecoration(labelText: \u0026#39;Username\u0026#39;), autofillHints: [AutofillHints.username] ), SizedBox(height: 16.0), TextField( controller: _passwordController, decoration: InputDecoration(labelText: \u0026#39;Password\u0026#39;), obscureText: true, autofillHints: [AutofillHints.password] ), SizedBox(height: 16.0), ElevatedButton( onPressed: () { // Handle login logic here print(\u0026#39;Username: ${_usernameController.text}\u0026#39;); print(\u0026#39;Password: ${_passwordController.text}\u0026#39;); }, child: Text(\u0026#39;Login\u0026#39;) )])))); } } In case of the username, it might make sense to use AutofillHints.username and AutofillHints.email if an email is used as a username.\nIt’s also possible to use birthdays, address data, credit card information, or other stuff. Just change the autofillHints property accordingly. Also check the troubleshoot hints at the bottom of the page when autofill is not available although you added it.\nHere is the full list of all constants defined in the AutofillHints class with an explanation:\naddressCity → The input field expects an address locality (city/town). addressCityAndState → The input field expects a city name combined with a state name. addressState → The input field expects a region/state. birthday → The input field expects a person\u0026#39;s full birth date. birthdayDay → The input field expects a person\u0026#39;s birth day(of the month). birthdayMonth → The input field expects a person\u0026#39;s birth month. birthdayYear → The input field expects a person\u0026#39;s birth year. countryCode → The input field expects an ISO 3166-1-alpha-2 country code. countryName → The input field expects a country name. creditCardExpirationDate → The input field expects a credit card expiration date. creditCardExpirationDay → The input field expects a credit card expiration day. creditCardExpirationMonth → The input field expects a credit card expiration month. creditCardExpirationYear → The input field expects a credit card expiration year. creditCardFamilyName → The input field expects the holder\u0026#39;s last/family name as given on a credit card. creditCardGivenName → The input field expects the holder\u0026#39;s first/given name as given on a credit card. creditCardMiddleName → The input field expects the holder\u0026#39;s middle name as given on a credit card. creditCardName → The input field expects the holder\u0026#39;s full name as given on a credit card. creditCardNumber → The input field expects a credit card number. creditCardSecurityCode → The input field expects a credit card security code. creditCardType → The input field expects the type of a credit card, for example \u0026#34;Visa\u0026#34;. email → The input field expects an email address. familyName → The input field expects a person\u0026#39;s last/family name. fullStreetAddress → The input field expects a street address that fully identifies a location. gender → The input field expects a gender. givenName → The input field expects a person\u0026#39;s first/given name. impp → The input field expects a URL representing an instant messaging protocol endpoint. jobTitle → The input field expects a job title. language → The input field expects the preferred language of the user. location → The input field expects a location, such as a point of interest, an address,or another way to identify a location. middleInitial → The input field expects a person\u0026#39;s middle initial. middleName → The input field expects a person\u0026#39;s middle name. name → The input field expects a person\u0026#39;s full name. namePrefix → The input field expects a person\u0026#39;s name prefix or title, such as \u0026#34;Dr.\u0026#34;. nameSuffix → The input field expects a person\u0026#39;s name suffix, such as \u0026#34;Jr.\u0026#34;. newPassword → The input field expects a newly created password for save/update. newUsername → The input field expects a newly created username for save/update. nickname → The input field expects a nickname. oneTimeCode → The input field expects a SMS one-time code. organizationName → The input field expects an organization name corresponding to the person, address, or contact information in the other fields associated with this field. password → The input field expects a password. photo → The input field expects a photograph, icon, or other image corresponding to the company, person, address, or contact information in the other fields associated with this field. postalAddress → The input field expects a postal address. postalAddressExtended → The input field expects an auxiliary address details. postalAddressExtendedPostalCode → The input field expects an extended ZIP/POSTAL code. postalCode → The input field expects a postal code. streetAddressLevel1 → The first administrative level in the address. This is typically the province in which the address is located. In the United States, this would be the state. In Switzerland, the canton. In the United Kingdom, the post town. streetAddressLevel2 → The second administrative level, in addresses with at least two of them. In countries with two administrative levels, this would typically be the city, town, village, or other locality in which the address is located. streetAddressLevel3 → The third administrative level, in addresses with at least three administrative levels. streetAddressLevel4 → The finest-grained administrative level, in addresses which have four levels. streetAddressLine1 → The input field expects the first line of a street address. streetAddressLine2 → The input field expects the second line of a street address. streetAddressLine3 → The input field expects the third line of a street address. sublocality → The input field expects a sublocality. telephoneNumber → The input field expects a telephone number. telephoneNumberAreaCode → The input field expects a phone number\u0026#39;s area code, with a country -internal prefix applied if applicable. telephoneNumberCountryCode → The input field expects a phone number\u0026#39;s country code. telephoneNumberDevice → The input field expects the current device\u0026#39;s phone number, usually for Sign Up / OTP flows. telephoneNumberExtension → The input field expects a phone number\u0026#39;s internal extension code. telephoneNumberLocal → The input field expects a phone number without the country code and area code components. telephoneNumberLocalPrefix → The input field expects the first part of the component of the telephone number that follows the area code, when that component is split into two components. telephoneNumberLocalSuffix → The input field expects the second part of the component of the telephone number that follows the area code, when that component is split into two components. telephoneNumberNational → The input field expects a phone number without country code. transactionAmount → The amount that the user would like for the transaction (e.g. when entering a bid or sale price). transactionCurrency → The currency that the user would prefer the transaction to use, in ISO 4217 currency code. url → The input field expects a URL. username → The input field expects a username or an account name. Below you can see an example of how the integration looks like when used in a Flutter web app with Bitwarden password manager.\nExample of password autofill with Bitwarden in a Flutter web app\nConclusion In this article you learned how to add password manager support to your Flutter apps. Make the life of your users easier by adding shortcuts to enter usernames, passwords, street addresses, or credit card data.\n","permalink":"https://quickcoder.org/posts/how-to-add-password-manager-support-to-your-flutter-apps/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eLet’s welcome Bitwarden, KeePass, LastPass, and others. Here is how to add password manager support to your Flutter apps. In addition, you can use other autofill options for credit cards, birthdays, address data, and more in the same way!\u003c/p\u003e\n\u003cp\u003ePassword managers save us time and help choosing stronger and more diverse passwords in general. Instead of remembering hundreds of credentials for all your sites, a master password is enough. In this article I will show you how to add password manager support to your Flutter apps. In addition, you also can configure this to support autofills for emails, username, birthdays, address data, or credit card information!\u003c/p\u003e","title":"How To Add Password Manager Support To Your Flutter Apps"},{"content":"Introduction Here is a deep dive into GPS, permissions, and manifests. Learn how to track your location in a Flutter app. Find out how to collect location data while your app is in the background. There are also tips included to prevent the OS from terminating your app after some time.\nWorking with location data in mobile-aware apps is a common use case in the current app ecosystem. In this deep dive I will show you everything you need to know about how to track your location in a Flutter app. No matter if you want a to put a pin on a map with the current location or track a device continuously for hours, everything is possible.\nSet up and preparations There are different Flutter packages available that do the heavy lifting for us. The two most advanced ones are location and geolocator. They have a similar feature set and if you know how to handle one, you also will have no problem with the other. However, this article will rely on the package geolocator.\nTo install the package, add it to your pubspec.yaml manually or with the command flutter pub add geolocator. For more information about how to add packages in Flutter apps, read this article.\nAndroid Your Android app needs to declare certain capabilities to access location data. Those are defined in the AndroidManifest.xml. Navigate to the android\\app\\src\\main\\ folder, open the file, and add the following permissions inside the manifest tag:\n\u0026lt;manifest xmlns:android=\u0026#34;\u0026lt;http://schemas.android.com/apk/res/android\u0026gt;\u0026#34;\u0026gt; \u0026lt;uses-permission android:name=\u0026#34;android.permission.ACCESS_COARSE_LOCATION\u0026#34; /\u0026gt; \u0026lt;uses-permission android:name=\u0026#34;android.permission.ACCESS_FINE_LOCATION\u0026#34; /\u0026gt; \u0026lt;application android:label=\u0026#34;your awesome app\u0026#34; android:name=\u0026#34;${applicationName}\u0026#34;\u0026gt; \u0026lt;!-- ... --\u0026gt; \u0026lt;/application\u0026gt; \u0026lt;/manifest\u0026gt; ACCESS_COARSE_LOCATION is the minimal requirement and returns a rough location estimation that can be up to 100m off your actual position. ACCESS_FINE_LOCATION is more detailed and can be used for routing and navigation. You need to define COARSE as well if you want to use FINE.\nIf both permissions are declared, Android will ask the user to select a permission (see image).\nScreenshot of an Android app requesting the device’s approximate location with the ACCESS_COARSE_LOCATION permission defined in the AndroidManifest.xml (left) and screenshot of an Android app requesting the device’s exact location with the ACCESS_FINE_LOCATION permission defined in the AndroidManifest.xml. The user can select which permission should be granted.\niOS For iOS, add the following keys to your ios/Runner/Info.plist to access GPS data:\n\u0026lt;key\u0026gt;NSLocationWhenInUseUsageDescription\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;This app needs access to location.\u0026lt;/string\u0026gt; Make sure to add a valid reason or your app will be rejected by Apple in their App Store.\nScreenshot of a mobile iOS app requesting access to location services. The reason phrase from the configuration file is displayed in the request.\nWith that, your Flutter app is ready to work with the position information provided by the mobile devices.\nRequest permissions The package contains useful helper methods to check if the location services are enabled and to request permission to use them.\nWith checkPermission() you can verify that the app has all needed permissions to access the location data of the device. The result is an self-descriptive enumeration.\nTo request permission, use the method requestPermission(). This triggers a selection dialog for the user or returns the previous selection.\nThe last useful helper method is the isLocationServiceEnabled() method which should be self-explaining.\nSubscribe to location updates Here is a full code example to subscribe to location updates:\nimport \u0026#39;package:geolocator/geolocator.dart\u0026#39;; final hasPermission = await Geolocator.checkPermission(); if (hasPermission == LocationPermission.deniedForever) { print(\u0026#34;The permission to access the device location was denied forever!\u0026#34;); return; } if (hasPermission == LocationPermission.denied){ final hasFinalPermission = await Geolocator.requestPermission(); if (hasFinalPermission == LocationPermission.denied || hasFinalPermission == LocationPermission.deniedForever) { print(\u0026#34;The permission to access the device location was denied!\u0026#34;); return false; } } if (!await Geolocator.isLocationServiceEnabled()) { print(\u0026#34;The location services of the device are not enabled!\u0026#34;); return; } final subscription = Geolocator.getPositionStream().listen((pos) { print(\u0026#34;${pos.latitude}, ${pos.longitude}\u0026#34;); }); The first step is to verify the permission. If it was denied, we ask again. This opens a dialog for the user to select the permission for our app.\nIn addition, we check if location services are enabled. If everything is fine, we subscribe to the position stream to get location updates.\nConfigure location settings You can configure the location settings of the location stream. Therefor, you can either use the base class LocationSettings or specialized classes for the mobile platforms, AndroidSettings and AppleSettings.\nThe most basic configuration values are a distance filter and an accuracy setting. The distance filter filters location updates that are too close to the last position. It is specified in meters. The accuracy setting describes how precise the location should be retrieved. This can range from a few 100 meters to as close as 1 or 2 meters.\nThe classes AndroidSettings and AppleSettings contain platform-specific settings.\nfinal settings = LocationSettings(...); final subscription = Geolocator .getPositionStream(locationSettings: settings) .listen((pos) { print(\u0026#34;${pos.latitude}, ${pos.longitude}\u0026#34;); }); Background updates Sometimes, you might want to get location updates even if your app is not running in the foreground. Geolocator can be configured to also work under these circumstances.\n🔔 Terminology\nWe assume an app has three states: Foreground, background, and terminated.\nForeground means that the app is open and visible on the device screen. Background means the app was started previously but is not visible. A terminated is not running anymore because it was terminated by the user or by the operating system.\nTo support background updates, we need to add more permissions in the AndroidManifest.xml and the Info.plist.\nHere is the updated AndroidManifest.xml:\n\u0026lt;manifest xmlns:android=\u0026#34;\u0026lt;http://schemas.android.com/apk/res/android\u0026gt;\u0026#34;\u0026gt; \u0026lt;uses-permission android:name=\u0026#34;android.permission.ACCESS_COARSE_LOCATION\u0026#34; /\u0026gt; \u0026lt;uses-permission android:name=\u0026#34;android.permission.ACCESS_FINE_LOCATION\u0026#34; /\u0026gt; \u0026lt;uses-permission android:name=\u0026#34;android.permission.ACCESS_BACKGROUND_LOCATION\u0026#34; /\u0026gt; \u0026lt;uses-permission android:name=\u0026#34;android.permission.FOREGROUND_SERVICE\u0026#34; /\u0026gt; \u0026lt;uses-permission android:name=\u0026#34;android.permission.FOREGROUND_SERVICE_LOCATION\u0026#34; /\u0026gt; \u0026lt;application android:label=\u0026#34;your awesome app\u0026#34; android:name=\u0026#34;${applicationName}\u0026#34;\u0026gt; \u0026lt;!-- ... --\u0026gt; \u0026lt;/application\u0026gt; \u0026lt;/manifest\u0026gt; And this is what your Info.plist should contain:\n\u0026lt;key\u0026gt;NSLocationWhenInUseUsageDescription\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;This app needs access to location.\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;UIBackgroundModes\u0026lt;/key\u0026gt; \u0026lt;array\u0026gt; \u0026lt;string\u0026gt;location\u0026lt;/string\u0026gt; \u0026lt;/array\u0026gt; Have a look at the geolocator documentation for more details!\nAnd here is how you activate background location updates in the code. This is only needed for iOS, Android works without any code changes!\nfinal settings = AppleSettings( showBackgroundLocationIndicator: true, allowBackgroundLocationUpdates: true); final subscription = Geolocator .getPositionStream(locationSettings: settings) .listen(...); Now, you can use another app while your location tracker runs in the background. It still receives location updates and can handle them.\nThe operating systems notify the user in different ways that there is an app running in the background, accessing location data, and probably causing increased battery drain.\nOn Android, the location services icon is permanently shown in the notification bar. In addition, the notification center shows a list of background apps at the bottom.\nScreenshot of the Android notification center with an active app indicator at the bottom (left) and screenshot of the Android notification center with a detail view of the active app indicator (right)\nOn iOS, the notification bar also shows a permanent icon for active location services. You can see details about the app and the used services when you swipe down.\nScreenshot of an iOS simulator running an app in the background (left) and screenshot of an iOS simulator running an app in the background with details (right)\nBe aware that the operating system decides when an app is terminated depending on the settings and the available resources. If you want to maximize chances that your apps runs smoothly in the background, read the tips in the next section.\nKeep your app alive A key marketing factor of smartphones is battery life. Of course, the vendors and Google try everything to make the battery last as long as possible. That’s why apps in the background are usually shut down when they consume too much energy or when resources like storage and computing power are limited.\nSome vendors even add custom logic apart from the rules of Google to determine when an app needs to stop. This means that it also depends on the vendor what actions you and your users need to take to make your app run successfully. Check out this website for vendor-specific instructions. Here are some general tips:\nPause app activitiy if unused (Android) Disable this setting of your app to prevent Android from stopping it. You cannot do this yourself, only the user can.\nGo to Settings → Apps → Your app to find the switch at the bottom of the page.\nScreenshot of the Android app settings dialog\nSet app battery usage to unrestricted (Android) Guide your users to activate the option Unrestricted to allow your app consume more battery while in the background.\nGo to Settings → Apps → Your app → App battery usage to find the setting.\nScreenshot of the Android app battery usage dialog\nAdd a permanent foreground notification (Android) A permanent notification is Android’s official way of telling the users that there is something happening in the background. Here is how to configure it with the geolocator package:\nfinal settings = AndroidSettings( foregroundNotificationConfig: ForegroundNotificationConfig( notificationText: \u0026#34;Doing some heavy work ... please stand by!\u0026#34;, notificationTitle: \u0026#34;Background Service\u0026#34;)); final subscription = Geolocator .getPositionStream(locationSettings: settings) .listen(...); Screenshot of the Android notification for an app running in the background\nIn case, your notification is not visible, try the following approaches from this thread:\nAdd a new permission for notifications to the AndroidManifest.xml file:\n\u0026lt;uses-permission android:name=\u0026#34;android.permission.POST_NOTIFICATIONS\u0026#34;/\u0026gt; Then, add permission_handler and request the permission for notifications in your app.\nawait Permission.notification.request(); If it still doesn’t work, try the package flutter_local_notifications. The code below recreates the notification channel so that the permanent notification should now be visible.\nimport \u0026#39;package:flutter_local_notifications/flutter_local_notifications.dart\u0026#39;; const AndroidNotificationChannel backgroundLocationChannel = AndroidNotificationChannel( \u0026#39;geolocator_channel_01\u0026#39;, // id \u0026#39;Background Location\u0026#39;, // name description: \u0026#39;Background Location Notification Channel\u0026#39;, // description importance: Importance.min, ); final FlutterLocalNotificationsPlugin plugin = FlutterLocalNotificationsPlugin(); await plugin .resolvePlatformSpecificImplementation\u0026lt;AndroidFlutterLocalNotificationsPlugin\u0026gt;() ?.createNotificationChannel(backgroundLocationChannel); Set appropriate activitiy type (iOS) The AppleSettings class in the geolocator package has a property activityType. With that you can tell the operating system what type of app you build so that the system can decide how to handle background updates.\nConclusion In this article you learned everything you about how to track your location in a Flutter app. By using established packages and following the setup instructions, you shouldn’t run into problems. For more advanced use cases, we also talked about background operations and how you can ensure that your app is not terminated by the operating system.\n","permalink":"https://quickcoder.org/posts/how-to-track-your-location-in-a-flutter-app/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere is a deep dive into GPS, permissions, and manifests. Learn how to track your location in a Flutter app. Find out how to collect location data while your app is in the background. There are also tips included to prevent the OS from terminating your app after some time.\u003c/p\u003e\n\u003cp\u003eWorking with location data in mobile-aware apps is a common use case in the current app ecosystem. In this deep dive I will show you everything you need to know about how to track your location in a Flutter app. No matter if you want a to put a pin on a map with the current location or track a device continuously for hours, everything is possible.\u003c/p\u003e","title":"How To Track Your Location In A Flutter App"},{"content":"Introduction Here is my practical guide about Riverpod. Learn how to use Riverpod in Flutter apps with simple code examples. Explore AsyncNotifierProvider, NotifierProvider, and FutureProvider in real-world scenarios.\nRiverpod is probably one of the top 2 state management solutions in Flutter apps together with BLoC according to my observations. In this guide, I want to show you some real-world use cases and how you can implement them with Riverpod. I assume you already used Riverpod before and gained some first-hand experience. So, let’s get started on how to use Riverpod in Flutter apps.\nBefore we begin, here is what I am going to cover:\nFetch data with AsyncNotifierProvider\nFetch data with FutureProvider\nFilter data with multiple providers\nLoad more items in a list with AsyncNotifierProvider\nRefresh a list with FutureProvider\nSort data with AsyncNotifierProvider\nThe code examples use the Riverpod Architecture presented by codewithandrea. It’s my favorite approach but you are free to use whatever you like. It consists of the layers data (getting data from APIs, files, etc.), domain (model classes), presentation (UI widgets and controllers), and application (optional abstraction layer for complex business logic). I will only talk briefly about those so check out the linked articles to learn more.\n💡 Tip\nI am not using any annotations or the riverpod generator in the examples!\nRead here for information about the Riverpod Generator.\nFetch data with AsyncNotifierProvider In this example, we assume that we want to read some data from an API and display it in our app. That’s probably the most common use case for the majority of apps around. Let’s see how this can be done:\nData First, we need to get some data. I created dummy repository classes for all code examples. In this case, it’s a class that returns some Article objects. Alongside we have a simple Provider to access the class.\nimport \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/fetch_with_async_notifier_provider_example/domain/article.dart\u0026#39;; final articleRepositoryProvider = Provider((ref) =\u0026gt; ArticleRepository()); class ArticleRepository { Future\u0026lt;List\u0026lt;Article\u0026gt;\u0026gt; fetchArticles() async { await Future.delayed(const Duration(seconds: 2)); return [ const Article( id: \u0026#34;1\u0026#34;, title: \u0026#39;Article 1\u0026#39;, body: \u0026#39;This is article 1\u0026#39;, ), const Article( id: \u0026#34;2\u0026#34;, title: \u0026#39;Article 2\u0026#39;, body: \u0026#39;This is article 2\u0026#39;, ), const Article( id: \u0026#34;3\u0026#34;, title: \u0026#39;Article 3\u0026#39;, body: \u0026#39;This is article 3\u0026#39;, ), ]; } } Domain Here is the corresponding Article object with fromJson, toJson, and Equatable implementation. They are not required for these examples but I tend to have them always. Just a coding habit of mine 🙂\nimport \u0026#39;package:equatable/equatable.dart\u0026#39;; class Article extends Equatable { final String id; final String title; final String body; const Article({ required this.id, required this.title, required this.body, }); factory Article.fromJson(Map\u0026lt;String, dynamic\u0026gt; json) { return Article( id: json[\u0026#39;id\u0026#39;], title: json[\u0026#39;title\u0026#39;], body: json[\u0026#39;body\u0026#39;], ); } Map\u0026lt;String, dynamic\u0026gt; toJson() { return { \u0026#39;id\u0026#39;: id, \u0026#39;title\u0026#39;: title, \u0026#39;body\u0026#39;: body, }; } @override List\u0026lt;Object?\u0026gt; get props =\u0026gt; [id, title, body]; } Presentation Now, let’s look at the UI layer. Here we have two classes, the ArticleListController and the ArticleListWidget.\nThe widget is a ConsumerStatefulWidget (Riverpod version of a StatefulWidget) and the State class extends ConsumerState\u0026lt;T\u0026gt; instead of State\u0026lt;T\u0026gt;. In the build() method, we watch the controller and based on the state we display the corresponding content with the when() method. To kick things off initially, we use the initState() method and call the controller to fetch data.\nimport \u0026#39;package:flutter/material.dart\u0026#39;; import \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/fetch_with_async_notifier_provider_example/presentation/article_list_controller.dart\u0026#39;; class ArticleListWidget extends ConsumerStatefulWidget { const ArticleListWidget({super.key}); @override ConsumerState\u0026lt;ConsumerStatefulWidget\u0026gt; createState() =\u0026gt; _State(); } class _State extends ConsumerState\u0026lt;ArticleListWidget\u0026gt; { @override void initState() { super.initState(); Future.delayed(const Duration(seconds: 1)).then((_) async =\u0026gt; await ref.read(articleListControllerProvider.notifier).fetchArticles()); } @override Widget build(BuildContext context) { final articleState = ref.watch(articleListControllerProvider); return Scaffold( body: articleState.when( data: (data) =\u0026gt; ListView( children: data.map((item) =\u0026gt; Text(item.title)).toList()), loading: () =\u0026gt; const Center(child: CircularProgressIndicator()), error: (error, stackTrace) =\u0026gt; Center(child: Text(\u0026#39;Error: $error\u0026#39;)))); } } The controller extends AutoDisposeAsyncNotifier\u0026lt;List\u0026lt;Article\u0026gt;\u0026gt; (because we want to display a list of articles) which disposes of the controller automatically when it is removed from the widget tree (no memory leaks!). The build() method returns an empty list. To actually get data, we need to call the fetchArticles() method. It will return a loading state first and eventually the data from the repository.\nAn AsyncNotifierProvider expects two type arguments: The controller (ArticleListController) class and the type of the controller class (List\u0026lt;Article\u0026gt;). It connects the UI layer with the data layer.\nimport \u0026#39;dart:async\u0026#39;; import \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/fetch_with_async_notifier_provider_example/domain/article.dart\u0026#39;; import \u0026#39;package:riverpod_example/fetch_with_async_notifier_provider_example/data/article_repository.dart\u0026#39;; final articleListControllerProvider = AsyncNotifierProvider.autoDispose\u0026lt;ArticleListController, List\u0026lt;Article\u0026gt;\u0026gt;( () =\u0026gt; ArticleListController()); class ArticleListController extends AutoDisposeAsyncNotifier\u0026lt;List\u0026lt;Article\u0026gt;\u0026gt; { @override FutureOr\u0026lt;List\u0026lt;Article\u0026gt;\u0026gt; build() =\u0026gt; []; Future\u0026lt;void\u0026gt; fetchArticles() async { state = const AsyncValue.loading(); state = await AsyncValue.guard( ref.read(articleRepositoryProvider).fetchArticles); } } To summarize the flow:\nThe UI watches the controller The controller uses the repository to grab data To kick things off initially, we trigger the controller once to fetch data 💡 Tip\nNo idea about AsyncValue.guard?\nIt’s just a try-catch alternative. For details, read this article.\nWhile this solutions works, it has some drawbacks.\nA lot of code for just fetching some data The controller needs to be triggered manually I will address both problems in the next example. But you get the point that there are multiple ways to achieve your goals with Riverpod. It takes practice to be really efficient.\nExample video of fetching data with an AsyncNotifierProvider\nHere is the link to the source code of this example.\nFetch data with FutureProvider Now we have the exact same use case as in the example before. But we are going to use FutureProvider instead of AsyncNotifierProvider this time. In the end, you’ll see the difference and know why FutureProvider is better suited for this scenario. Here are the details:\nData A similar repository like before. But this time, it’s about posts and not articles. Apart from the name, they are identical.\nimport \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/fetch_with_future_provider_example/domain/post.dart\u0026#39;; final postRepositoryProvider = Provider((ref) =\u0026gt; PostRepository()); class PostRepository { Future\u0026lt;List\u0026lt;Post\u0026gt;\u0026gt; fetchPosts() async { await Future.delayed(const Duration(seconds: 2)); return [ const Post( id: \u0026#34;1\u0026#34;, title: \u0026#39;Article 1\u0026#39;, body: \u0026#39;This is article 1\u0026#39;, ), const Post( id: \u0026#34;2\u0026#34;, title: \u0026#39;Article 2\u0026#39;, body: \u0026#39;This is article 2\u0026#39;, ), const Post( id: \u0026#34;3\u0026#34;, title: \u0026#39;Article 3\u0026#39;, body: \u0026#39;This is article 3\u0026#39;, ), ]; } } Domain The corresponding model class looks like this:\nimport \u0026#39;package:equatable/equatable.dart\u0026#39;; class Post extends Equatable { final String id; final String title; final String body; const Post({ required this.id, required this.title, required this.body, }); factory Post.fromJson(Map\u0026lt;String, dynamic\u0026gt; json) { return Post( id: json[\u0026#39;id\u0026#39;], title: json[\u0026#39;title\u0026#39;], body: json[\u0026#39;body\u0026#39;], ); } Map\u0026lt;String, dynamic\u0026gt; toJson() { return { \u0026#39;id\u0026#39;: id, \u0026#39;title\u0026#39;: title, \u0026#39;body\u0026#39;: body, }; } @override List\u0026lt;Object?\u0026gt; get props =\u0026gt; [id, title, body]; } Presentation The widget is now a ConsumerWidget (Riverpod version of a StatelessWidget) instead of a ConsumerStatefulWidget, because we don’t need the initState() method anymore. The code is already shorter and more performant.\nimport \u0026#39;package:flutter/material.dart\u0026#39;; import \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/fetch_with_future_provider_example/presentation/post_list_controller.dart\u0026#39;; class PostListWidget extends ConsumerWidget { const PostListWidget({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final postsState = ref.watch(postListControllerProvider); return Scaffold( body: postsState.when( data: (data) =\u0026gt; ListView( children: data.map((item) =\u0026gt; Text(item.title)).toList()), loading: () =\u0026gt; const Center(child: CircularProgressIndicator()), error: (error, stackTrace) =\u0026gt; Center(child: Text(\u0026#39;Error: $error\u0026#39;)))); } } And look at the controller! It’s just an auto-disposable FutureProvider of type List\u0026lt;Post\u0026gt; in 2 lines of code and it does the same as the controller in the previous example including an initial fetch without manual interaction. A FutureProvider gives you things automatically (like a loading state) while an AsyncNotifierProvider requires setting the state manually for that. We don’t need a specialized controller class with this approach.\nimport \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/fetch_with_future_provider_example/domain/post.dart\u0026#39;; import \u0026#39;package:riverpod_example/fetch_with_future_provider_example/data/post_repository.dart\u0026#39;; final postListControllerProvider = FutureProvider.autoDispose\u0026lt;List\u0026lt;Post\u0026gt;\u0026gt;( (ref) =\u0026gt; ref.read(postRepositoryProvider).fetchPosts()); You see that this approach is much shorter, easier to read and understand, and even faster than the previous example. A FutureProvider works best if you only want to call async methods from other layers. When you have more logic in your controller, choose an AsyncNotifierProvider instead.\nExample video of fetching data with a FutureProvider\nFollow the link to the code of this example.\nFilter data with multiple providers After the initial examples, let’s make it a bit more complex. In this case, we fetch data and implement a filter function. For that, we will combine a NotifierProvider and an AsyncNotifierProvider. Let’s explore how that can look like:\nData Once again, we have a simple repository that returns some data to play with.\nimport \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/filter_data_multiple_providers_example/domain/book.dart\u0026#39;; final bookRepositoryProvider = Provider\u0026lt;BookRepository\u0026gt;((ref) { return BookRepository(); }); class BookRepository { Future\u0026lt;List\u0026lt;Book\u0026gt;\u0026gt; fetchBooks() async { await Future.delayed(const Duration(seconds: 1)); return [ const Book(title: \u0026#39;Book 1\u0026#39;, author: \u0026#39;Author 1\u0026#39;, price: 9.99), const Book(title: \u0026#39;Book 2\u0026#39;, author: \u0026#39;Author 2\u0026#39;, price: 19.99), const Book(title: \u0026#39;Book 3\u0026#39;, author: \u0026#39;Author 3\u0026#39;, price: 29.99), const Book(title: \u0026#39;Book 4\u0026#39;, author: \u0026#39;Author 4\u0026#39;, price: 39.99), ]; } } Domain The Book class looks like this:\nimport \u0026#39;package:equatable/equatable.dart\u0026#39;; class Book extends Equatable { final String title; final String author; final double price; const Book({ required this.title, required this.author, required this.price, }); @override List\u0026lt;Object?\u0026gt; get props =\u0026gt; [title, author, price]; factory Book.fromJson(Map\u0026lt;String, dynamic\u0026gt; json) { return Book( title: json[\u0026#39;title\u0026#39;] as String, author: json[\u0026#39;author\u0026#39;] as String, price: json[\u0026#39;price\u0026#39;] as double, ); } Map\u0026lt;String, dynamic\u0026gt; toJson() { return { \u0026#39;title\u0026#39;: title, \u0026#39;author\u0026#39;: author, \u0026#39;price\u0026#39;: price, }; } } And we also this enum to represent the filter options:\nenum FilterOptionEnum { priceMoreThan5, priceMoreThan15, priceMoreThan25, priceMoreThan35 } For simplicity, the user can only filter the books by price. Of course, you can easily extend this to support other properties if you get the idea.\nApplication In this example we make use of the optional application layer because the filter logic does not belong to the presentation layer. Here is the FilterService class that filters a list of books based on the given filter value. We expose the service with a provider so that we can use it in the controller.\nimport \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/filter_data_multiple_providers_example/domain/book.dart\u0026#39;; import \u0026#39;package:riverpod_example/filter_data_multiple_providers_example/domain/filter_option_enum.dart\u0026#39;; final filterServiceProvider = Provider\u0026lt;FilterService\u0026gt;((ref) { return FilterService(); }); class FilterService { FilterService(); Future\u0026lt;List\u0026lt;Book\u0026gt;\u0026gt; filterBooks( List\u0026lt;Book\u0026gt; books, FilterOptionEnum filter) async { switch (filter) { case FilterOptionEnum.priceMoreThan5: return books.where((book) =\u0026gt; book.price \u0026gt; 5).toList(); case FilterOptionEnum.priceMoreThan15: return books.where((book) =\u0026gt; book.price \u0026gt; 15).toList(); case FilterOptionEnum.priceMoreThan25: return books.where((book) =\u0026gt; book.price \u0026gt; 25).toList(); case FilterOptionEnum.priceMoreThan35: return books.where((book) =\u0026gt; book.price \u0026gt; 35).toList(); } } } In addition, we have a Notifier that holds the currently selected filter option by the user. Here is how you can implement it:\nimport \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/filter_data_multiple_providers_example/domain/filter_option_enum.dart\u0026#39;; final filterOptionProvider = NotifierProvider\u0026lt;FilterOption, FilterOptionEnum\u0026gt;(FilterOption.new); class FilterOption extends Notifier\u0026lt;FilterOptionEnum\u0026gt; { @override build() =\u0026gt; FilterOptionEnum.priceMoreThan5; void setFilter(FilterOptionEnum filter) { state = filter; } } By default, the filter is set to priceMoreThan5 by the build() method. To update it, there is the setFilter() method that updates the state property.\nPresentation Now, here is some UI code. There are 4 buttons for the different filter options. Pressing the button calls the setFilter() method of the filterOptionProvider. The controller watches this provider and returns an updated list of books after calling the filter service.\nimport \u0026#39;package:flutter/material.dart\u0026#39;; import \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/filter_data_multiple_providers_example/application/filter_option_provider.dart\u0026#39;; import \u0026#39;package:riverpod_example/filter_data_multiple_providers_example/domain/filter_option_enum.dart\u0026#39;; import \u0026#39;package:riverpod_example/filter_data_multiple_providers_example/presentation/book_filter_controller.dart\u0026#39;; class BookListWidget extends ConsumerWidget { const BookListWidget({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final filterState = ref.watch(bookFilterControllerProvider); return Scaffold( body: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: () =\u0026gt; _setFilter(ref, FilterOptionEnum.priceMoreThan5), child: const Text(\u0026#34;Show books over 5€\u0026#34;)), ElevatedButton( onPressed: () =\u0026gt; _setFilter(ref, FilterOptionEnum.priceMoreThan15), child: const Text(\u0026#34;Show books over 15€\u0026#34;)), ElevatedButton( onPressed: () =\u0026gt; _setFilter(ref, FilterOptionEnum.priceMoreThan25), child: const Text(\u0026#34;Show books over 25€\u0026#34;)), ElevatedButton( onPressed: () =\u0026gt; _setFilter(ref, FilterOptionEnum.priceMoreThan35), child: const Text(\u0026#34;Show books over 35€\u0026#34;)), ]), filterState.when( data: (books) =\u0026gt; ListView( shrinkWrap: true, children: books .map((item) =\u0026gt; Text(\u0026#34;${item.title}, ${item.author}, ${item.price}\u0026#34;)) .toList()), loading: () =\u0026gt; const CircularProgressIndicator(), error: (error, stack) =\u0026gt; Text(\u0026#34;Error: $error\u0026#34;)) ])); } Future\u0026lt;void\u0026gt; _setFilter(WidgetRef ref, FilterOptionEnum filter) async { ref.read(filterOptionProvider.notifier).setFilter(filter); } } import \u0026#39;dart:async\u0026#39;; import \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/filter_data_multiple_providers_example/application/filter_option_provider.dart\u0026#39;; import \u0026#39;package:riverpod_example/filter_data_multiple_providers_example/application/filter_service.dart\u0026#39;; import \u0026#39;package:riverpod_example/filter_data_multiple_providers_example/domain/book.dart\u0026#39;; import \u0026#39;package:riverpod_example/filter_data_multiple_providers_example/data/book_repository.dart\u0026#39;; final bookFilterControllerProvider = AutoDisposeAsyncNotifierProvider\u0026lt;BookFilterController, List\u0026lt;Book\u0026gt;\u0026gt;( () =\u0026gt; BookFilterController()); class BookFilterController extends AutoDisposeAsyncNotifier\u0026lt;List\u0026lt;Book\u0026gt;\u0026gt; { @override FutureOr\u0026lt;List\u0026lt;Book\u0026gt;\u0026gt; build() async { final books = await ref.read(bookRepositoryProvider).fetchBooks(); final filter = ref.watch(filterOptionProvider); return await ref.read(filterServiceProvider).filterBooks(books, filter); } } Since the controller only uses the build() method, you’ll get results as soon as the widget is created initially. And you could replace the AsyncNotifier with a FutureProvider to make the code shorter as we have already learned before.\nShort recap\nNotifier holds current filter value Controller watches filter notifier, grabs data from the repository, and returns the filtered results Optional application layer is used to keep logic away from the controller All we do is set the filter and Riverpod does the rest Example video of filtering data with multiple providers\nLearn all details with the corresponding source code.\nLoad more items in a list with AsyncNotifierProvider Another common task is load more data in a list. Many social networks use inifinite scrolling, a technique to load more data once when user scrolls near the end of a list. Here is how you can do that with Riverpod:\nData Here is another repository that provides data for this example:\nimport \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/load_more_list_items_with_async_notifier_provider_example/domain/car.dart\u0026#39;; final carRepositoryProvider = Provider((ref) =\u0026gt; CarRepository()); class CarRepository { Future\u0026lt;List\u0026lt;Car\u0026gt;\u0026gt; fetchCars(int howMany) async { await Future.delayed(const Duration(seconds: 2)); return List.generate( howMany, (index) =\u0026gt; Car( horsepower: (index + 1) * 50, manufacturer: \u0026#39;Brand ${index + 1}\u0026#39;, model: \u0026#39;Model ${index + 1}\u0026#39;, year: 2022 - index, )); } } Domain And this is the corresponding model class:\nimport \u0026#39;package:equatable/equatable.dart\u0026#39;; class Car extends Equatable { final int horsepower; final String manufacturer; final String model; final int year; const Car({ required this.horsepower, required this.manufacturer, required this.model, required this.year, }); @override List\u0026lt;Object?\u0026gt; get props =\u0026gt; [horsepower, manufacturer, model, year]; factory Car.fromJson(Map\u0026lt;String, dynamic\u0026gt; json) { return Car( horsepower: json[\u0026#39;horsepower\u0026#39;] as int, manufacturer: json[\u0026#39;manufacturer\u0026#39;] as String, model: json[\u0026#39;model\u0026#39;] as String, year: json[\u0026#39;year\u0026#39;] as int, ); } Map\u0026lt;String, dynamic\u0026gt; toJson() { return { \u0026#39;horsepower\u0026#39;: horsepower, \u0026#39;manufacturer\u0026#39;: manufacturer, \u0026#39;model\u0026#39;: model, \u0026#39;year\u0026#39;: year, }; } } Application In this layer, we define a fetch count provider as a NotifierProvider. The notifier holds a number that indicates how many results the repository should return. By default, it’s 5 and there is a method to increase the number by 5.\nimport \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; final fetchCountProvider = NotifierProvider\u0026lt;FetchCount, int\u0026gt;(() { return FetchCount(); }); class FetchCount extends Notifier\u0026lt;int\u0026gt; { @override int build() { return 5; } void incBy5() { state = state + 5; } } Presentation The widget contains a button that increases the fetch count by calling the notifier method we defined earlier.\nimport \u0026#39;package:flutter/material.dart\u0026#39;; import \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/load_more_list_items_with_async_notifier_provider_example/application/fetch_count_provider.dart\u0026#39;; import \u0026#39;package:riverpod_example/load_more_list_items_with_async_notifier_provider_example/presentation/car_list_controller.dart\u0026#39;; class CarListWidget extends ConsumerWidget { const CarListWidget({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final carState = ref.watch(carListControllerProvider); return Scaffold( body: carState.when( data: (data) =\u0026gt; Column(children: [ ListView( shrinkWrap: true, children: data .map((item) =\u0026gt; Text( \u0026#34;${item.manufacturer}, ${item.horsepower}, ${item.model}, ${item.year}\u0026#34;)) .toList()), const SizedBox(height: 10), ElevatedButton( onPressed: () =\u0026gt; _loadMoreItems(ref), child: const Text(\u0026#34;Load more items\u0026#34;)), ]), loading: () =\u0026gt; const CircularProgressIndicator(), error: (error, stack) =\u0026gt; Text(\u0026#34;Error: $error\u0026#34;))); } void _loadMoreItems(WidgetRef ref) { ref.read(fetchCountProvider.notifier).incBy5(); } } The controller works similar to the one in the previous example. It watches the fetch count provider and passes the value to the repository which returns the exact amount of requested items.\nimport \u0026#39;dart:async\u0026#39;; import \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/load_more_list_items_with_async_notifier_provider_example/application/fetch_count_provider.dart\u0026#39;; import \u0026#39;package:riverpod_example/load_more_list_items_with_async_notifier_provider_example/domain/car.dart\u0026#39;; import \u0026#39;package:riverpod_example/load_more_list_items_with_async_notifier_provider_example/data/car_repository.dart\u0026#39;; final carListControllerProvider = AutoDisposeAsyncNotifierProvider\u0026lt;CarListController, List\u0026lt;Car\u0026gt;\u0026gt;( () =\u0026gt; CarListController()); class CarListController extends AutoDisposeAsyncNotifier\u0026lt;List\u0026lt;Car\u0026gt;\u0026gt; { @override FutureOr\u0026lt;List\u0026lt;Car\u0026gt;\u0026gt; build() async { final itemsCount = ref.watch(fetchCountProvider); final repository = ref.read(carRepositoryProvider); return await repository.fetchCars(itemsCount); } } If you followed the previous examples, you should see the similarities and differences by now.\nExample video of how to load more list items with a FutureProvider\nCheck out the example source code.\nRefresh a list with FutureProvider Pull to refresh is a common pattern in mobile apps and websites to reload a list. With Riverpods FutureProvider, this is implemented in minutes. Get ready for the details:\nData Another example, another dummy repository 🙂\nimport \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/refresh_list_items_with_future_provider_example/domain/animal.dart\u0026#39;; final animalRepositoryProvider = Provider((ref) =\u0026gt; AnimalRepository()); class AnimalRepository { Future\u0026lt;List\u0026lt;Animal\u0026gt;\u0026gt; fetch() async { await Future.delayed(const Duration(seconds: 2)); return [ Animal(name: \u0026#39;Lion\u0026#39;, age: DateTime.now().second, species: \u0026#39;Panthera leo\u0026#39;), Animal( name: \u0026#39;Elephant\u0026#39;, age: DateTime.now().microsecond, species: \u0026#39;Loxodonta africana\u0026#39;), Animal( name: \u0026#39;Giraffe\u0026#39;, age: DateTime.now().millisecond, species: \u0026#39;Giraffa camelopardalis\u0026#39;) ]; } } Domain This is the model class for the repository:\nimport \u0026#39;package:equatable/equatable.dart\u0026#39;; class Animal extends Equatable { final String name; final int age; final String species; const Animal({ required this.name, required this.age, required this.species, }); @override List\u0026lt;Object?\u0026gt; get props =\u0026gt; [name, age, species]; factory Animal.fromJson(Map\u0026lt;String, dynamic\u0026gt; json) { return Animal( name: json[\u0026#39;name\u0026#39;] as String, age: json[\u0026#39;age\u0026#39;] as int, species: json[\u0026#39;species\u0026#39;] as String, ); } Map\u0026lt;String, dynamic\u0026gt; toJson() { return { \u0026#39;name\u0026#39;: name, \u0026#39;age\u0026#39;: age, \u0026#39;species\u0026#39;: species, }; } } Presentation The widget contains a button that calls ref.invalidate() on a provider. Since we watch this provider, it will immediately recompute the return value and trigger a rebuild of the widget.\nimport \u0026#39;package:flutter/material.dart\u0026#39;; import \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/refresh_list_items_with_future_provider_example/presentation/animal_list_controller.dart\u0026#39;; class AnimalListWidget extends ConsumerWidget { const AnimalListWidget({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final animalState = ref.watch(animalListControllerProvider); return Scaffold( body: animalState.when( skipLoadingOnRefresh: false, data: (data) =\u0026gt; Column(children: [ ListView( shrinkWrap: true, children: data .map((item) =\u0026gt; Text( \u0026#34;${item.name}, ${item.age}, ${item.species}\u0026#34;)) .toList()), const SizedBox(height: 10), ElevatedButton( onPressed: () =\u0026gt; _refresh(ref), child: const Text(\u0026#34;Refresh\u0026#34;)), ]), loading: () =\u0026gt; const CircularProgressIndicator(), error: (error, stack) =\u0026gt; Text(\u0026#34;Error: $error\u0026#34;))); } void _refresh(WidgetRef ref) { ref.invalidate(animalListControllerProvider); } } The controller is a FutureProvider that calls the repository for data.\nimport \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/refresh_list_items_with_future_provider_example/domain/animal.dart\u0026#39;; import \u0026#39;package:riverpod_example/refresh_list_items_with_future_provider_example/data/animal_repository.dart\u0026#39;; final animalListControllerProvider = FutureProvider.autoDispose\u0026lt;List\u0026lt;Animal\u0026gt;\u0026gt;( (ref) async =\u0026gt; await ref.read(animalRepositoryProvider).fetch()); With ref.invalidate() or ref.refresh() you can force a provider to recalculate its value.\nExample video of how to refresh a FutureProvider\nRead more about the entire source code here.\nSort data with AsyncNotifierProvider The last example shows how you can sort data. Easy, but effective. Let’s jump right in:\nData Here is the data provider:\nimport \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/sort_data_with_async_notifier_provider_example/domain/person.dart\u0026#39;; final personRepositoryProvider = Provider((ref) =\u0026gt; PersonRepository()); class PersonRepository { Future\u0026lt;List\u0026lt;Person\u0026gt;\u0026gt; fetchPersons() async { await Future.delayed(const Duration(seconds: 1)); return [ const Person(id: 1, name: \u0026#39;John Doe\u0026#39;, age: 30), const Person(id: 2, name: \u0026#39;Jane Doe\u0026#39;, age: 25), const Person(id: 3, name: \u0026#39;Alice\u0026#39;, age: 35), const Person(id: 4, name: \u0026#39;Bob\u0026#39;, age: 40), ]; } } Domain And this is the domain model for the repository:\nimport \u0026#39;package:equatable/equatable.dart\u0026#39;; class Person extends Equatable { final int id; final String name; final int age; const Person({ required this.id, required this.name, required this.age, }); @override List\u0026lt;Object?\u0026gt; get props =\u0026gt; [id, name, age]; factory Person.fromJson(Map\u0026lt;String, dynamic\u0026gt; json) { return Person( id: json[\u0026#39;id\u0026#39;] as int, name: json[\u0026#39;name\u0026#39;] as String, age: json[\u0026#39;age\u0026#39;] as int, ); } Map\u0026lt;String, dynamic\u0026gt; toJson() { return { \u0026#39;id\u0026#39;: id, \u0026#39;name\u0026#39;: name, \u0026#39;age\u0026#39;: age, }; } } Additionally, we have an enum of sorting options:\nenum Sort { byName, byAge, byId } Application This is the service for sorting the persons based on the sorting option:\nimport \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/sort_data_with_async_notifier_provider_example/domain/person.dart\u0026#39;; import \u0026#39;package:riverpod_example/sort_data_with_async_notifier_provider_example/domain/sort.dart\u0026#39;; import \u0026#39;package:riverpod_example/sort_data_with_async_notifier_provider_example/data/person_repository.dart\u0026#39;; final sortServiceProvider = Provider((ref) =\u0026gt; SortService(ref)); class SortService { final ProviderRef ref; SortService(this.ref); Future\u0026lt;List\u0026lt;Person\u0026gt;\u0026gt; sortPersons(Sort sort) async { final allPersons = await ref.read(personRepositoryProvider).fetchPersons(); switch (sort) { case Sort.byName: allPersons.sort((a, b) =\u0026gt; a.name.compareTo(b.name)); case Sort.byAge: allPersons.sort((a, b) =\u0026gt; a.age.compareTo(b.age)); case Sort.byId: allPersons.sort((a, b) =\u0026gt; a.id.compareTo(b.id)); } return allPersons; } } Presentation In the UI, all the pieces come together. There are 3 buttons for the different sort options. They all call the controller which handles the sorting.\nimport \u0026#39;package:flutter/material.dart\u0026#39;; import \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/sort_data_with_async_notifier_provider_example/domain/sort.dart\u0026#39;; import \u0026#39;package:riverpod_example/sort_data_with_async_notifier_provider_example/presentation/person_sort_controller.dart\u0026#39;; class PersonListWidget extends ConsumerWidget { const PersonListWidget({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final sortState = ref.watch(personSortControllerProvider); return Scaffold( body: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: () =\u0026gt; _sortById(ref), child: const Text(\u0026#34;Sort by id\u0026#34;)), ElevatedButton( onPressed: () =\u0026gt; _sortByName(ref), child: const Text(\u0026#34;Sort by name\u0026#34;)), ElevatedButton( onPressed: () =\u0026gt; _sortByAge(ref), child: const Text(\u0026#34;Sort by age\u0026#34;)), ]), sortState.when( data: (data) =\u0026gt; ListView( shrinkWrap: true, children: data .map((item) =\u0026gt; Text(\u0026#34;${item.id}, ${item.name}, ${item.age}\u0026#34;)) .toList()), loading: () =\u0026gt; const CircularProgressIndicator(), error: (error, stack) =\u0026gt; Text(\u0026#34;Error: $error\u0026#34;)) ])); } Future\u0026lt;void\u0026gt; _sortById(WidgetRef ref) async { ref.read(personSortControllerProvider.notifier).sortPersons(Sort.byId); } Future\u0026lt;void\u0026gt; _sortByName(WidgetRef ref) async { ref.read(personSortControllerProvider.notifier).sortPersons(Sort.byName); } Future\u0026lt;void\u0026gt; _sortByAge(WidgetRef ref) async { ref.read(personSortControllerProvider.notifier).sortPersons(Sort.byAge); } } Here is the controller. Note that we don’t use an additional Notifier as in previous examples. Instead, the controller has a method sortPersons that expects the sorting option as an argument.\nimport \u0026#39;dart:async\u0026#39;; import \u0026#39;package:flutter_riverpod/flutter_riverpod.dart\u0026#39;; import \u0026#39;package:riverpod_example/sort_data_with_async_notifier_provider_example/application/sort_service.dart\u0026#39;; import \u0026#39;package:riverpod_example/sort_data_with_async_notifier_provider_example/domain/sort.dart\u0026#39;; import \u0026#39;package:riverpod_example/sort_data_with_async_notifier_provider_example/domain/person.dart\u0026#39;; final personSortControllerProvider = AutoDisposeAsyncNotifierProvider\u0026lt;PersonSortController, List\u0026lt;Person\u0026gt;\u0026gt;( () =\u0026gt; PersonSortController()); class PersonSortController extends AutoDisposeAsyncNotifier\u0026lt;List\u0026lt;Person\u0026gt;\u0026gt; { @override FutureOr\u0026lt;List\u0026lt;Person\u0026gt;\u0026gt; build() =\u0026gt; []; Future\u0026lt;void\u0026gt; sortPersons(Sort sort) async { state = const AsyncValue.loading(); state = await AsyncValue.guard( () async =\u0026gt; await ref.read(sortServiceProvider).sortPersons(sort)); } } This example shows a strict layer separation. The controller from the UI layer calls the SortService class in the application which calls the person repository in the data layer to retrieve data.\nExample video of how to sort data with AsyncProviderNotifier\nFollow this link for the full code.\nConclusion In this article, you learned how to use Riverpod in Flutter apps with real-world scenarios. For beginners, Riverpod can be a bit challenging. But once you get the idea behind it, you can build proper apps with less code than before.\nWith those examples, you should get the idea behind Riverpod. And remember: There are often multiple ways to achieve the desired results!\n","permalink":"https://quickcoder.org/posts/how-to-use-riverpod-in-flutter-apps/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere is my practical guide about Riverpod. Learn how to use Riverpod in Flutter apps with simple code examples. Explore AsyncNotifierProvider, NotifierProvider, and FutureProvider in real-world scenarios.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"http://riverpod.dev\"\u003eRiverpod\u003c/a\u003e is probably one of the top 2 state management solutions in Flutter apps together with \u003ca href=\"https://bloclibrary.dev/\"\u003eBLoC\u003c/a\u003e according to my observations. In this guide, I want to show you some real-world use cases and how you can implement them with Riverpod. I assume you already used Riverpod before and gained some first-hand experience. So, let’s get started on how to use Riverpod in Flutter apps.\u003c/p\u003e","title":"How To Use Riverpod In Flutter Apps - A Practical Approach"},{"content":"Introduction Here is how to manage a Flutter monorepo with multiple packages and melos instead of using shell scripts. Repository administration is really easy with this field-tested tool!\nI have some private Flutter packages in single repositories on GitHub. And because they are private, I had some authentication issues with transitive dependencies. But then someone guided me into another direction → monorepo. Here is a quick start guide of how to manage a Flutter monorepo with multiple packages and melos.\nTo be honest, I haven’t done that for years because it’s way easier to manage small repos. But thanks to melos, the effort is actually not that high for me. It only took me around an hour of learning and setup. However, I can add new packages without having to change anything in the configuration. It works out of the box.\nIn this article, I want to give you a short introduction of how I manage my monorepo with melos.\nMy goal For my purpose, I basically have this one goal\nNew code must not break anything\nChanges come via pull requests and can only be merged when the quality checks pass. Here is what I am aiming for\nAll tests pass (means running flutter test in every package)\nAll analyzers pass (means running flutter analyze in every package)\nI could write a shell script for that and it would be easy. But if the repo grows I might run into problems in the future. That’s why I tried melos.\nWhat is melos? Melos is a tool used for handling Dart projects that contain multiple packages, often called mono-repos. It is currently being developed and is used in projects like FlutterFire.\nMelos lets multiple packages operate within a single repository while still keeping them independent from each other. Its features include:\nAutomatically creating new versions and changelogs.\nAutomatically publishing packages to pub.dev.\nConnecting and setting up local packages.\nRunning commands at the same time across different packages.\nShowing a list of local packages and what they depend on. Melos is also very useful in CI/CD environments for automating complex tasks and solving challenges.\nCheck out the website for more details. They even support IDE integrations.\nMelos setup Install melos\nRun dart pub global activate melos and make sure to add it to your PATH variable so that you can access it from anywhere with your preferred CLI tool.\nCreate a pubspec.yaml in your root folder of your repository\nSet the name and environment properties (see below)\nAdd a melos dev dependency You can do this with the command dart pub add melos --dev.\nname: a_project environment: sdk: \u0026#39;\u0026gt;=3.1.3 \u0026lt;4.0.0\u0026#39; dev_dependencies: melos: ^6.0.0 4. Create a melos.yaml and configure your workspace\nTell melos about your repository structure like this so that it can find your packages.\nname: some_project packages: - core/** - services/** 5. Bootstrap your project\nmelos bootstrap does the job. Your packages are now properly linked and the fun stuff can begin.\nMelos is now successfully linked with all your packages. Let’s create some automation scripts!\nMelos scripts Let’s start with my final melos.yaml and then I explain what I did:\nname: my_project packages: - \u0026#34;core/**\u0026#34; - \u0026#34;services/**\u0026#34; command: bootstrap: usePubspecOverrides: true scripts: analyze: run: melos exec -- \u0026#34;flutter analyze\u0026#34; description: Analyze all packages test: run: melos exec --dir-exists=test -- \u0026#34;flutter test --coverage\u0026#34; description: Run tests for all packages In the scripts block you can define sections that melos can execute. I defined two sections analyze and test. To execute them, you can use the command melos run \u0026lt;script\u0026gt;.\nMy scripts are simple. Run flutter analyze and flutter test --coverage. Together with the melos exec command, this is already enough to reach my goals. They will be run on all found packages in the folders and subfolders of core or services in my repository.\nYou can learn more about scripts here.\nAutomate with CI and melos Now that we have configured melos and created scripts, we can integrate this entire process also in a build pipeline.\nFor GitHub, there is the bluefireteam/melos-action available which sets up melos in the CI environment and bootstraps it for you. Otherwise, you have to perform the install and setup steps like on your local machine to get it running.\nHere is an example GitHub Actions pipeline that uses melos. It runs the Dart analyzer on all packages and executes all tests.\nname: Pull Request on: pull_request: branches: - master jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Flutter uses: subosito/flutter-action@v2 with: flutter-version: \u0026#39;3.19.6\u0026#39; channel: \u0026#39;stable\u0026#39; - name: Setup melos uses: bluefireteam/melos-action@v3 - name: Run code analysis for all packages run: melos run analyze - name: Run unit tests for all packages run: melos run test With that you can ensure quality standards before any new code arrives in your repository.\nWhat else? Melos can even do more for you.\nCreate a changelog based on commit messages\nCreate releases\nPublish new package versions to pub.dev\nAll this can also be configured and automated. Check out the guides on the melos website for more details!\nConclusion In this article I gave a brief introduction of how to manage a Flutter monorepo with multiple packages and melos. You could, of course, just write your own shell scripts and leverage existing tools for that. But I assume that you will run into problems sooner or later. It’s not a coincidence that so many big players in the Flutter world use melos.\n","permalink":"https://quickcoder.org/posts/how-to-manage-a-flutter-monorepo-with-multiple-packages/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere is how to manage a Flutter monorepo with multiple packages and melos instead of using shell scripts. Repository administration is really easy with this field-tested tool!\u003c/p\u003e\n\u003cp\u003eI have some private Flutter packages in single repositories on GitHub. And because they are private, I had some \u003ca href=\"https://medium.com/long-sweet-valuable/the-struggle-with-flutter-packages-in-private-github-repositories-50e53852e4aa\"\u003eauthentication issues with transitive dependencies\u003c/a\u003e. But then someone guided me into another direction → \u003ca href=\"https://en.wikipedia.org/wiki/Monorepo\"\u003emonorepo\u003c/a\u003e. Here is a quick start guide of how to manage a Flutter \u003ca href=\"https://en.wikipedia.org/wiki/Monorepo\"\u003emonorepo\u003c/a\u003e with multiple packages and \u003ca href=\"https://melos.invertase.dev/\"\u003emelos\u003c/a\u003e.\u003c/p\u003e","title":"How To Manage A Flutter Monorepo With Multiple Packages"},{"content":"Introduction Backups are underrated … until you really need them. So here is how to backup your Cloud Firestore database. Forget about external tools or self-written Cloud Functions and use the provided tools instead!\nI store quite a lot of stuff in Firestore, mostly for personal use. In the last months, I often thought about backups. But I have to admit … I am lazy when it comes to boring stuff. However, some of the data is really valuable and will be lost in case of a disaster. So I finally stopped doing cooler things and investigate backup solutions. Here is my approach on how to backup your Cloud Firestore database.\n💡 Tip\nThis feature is still in a preview state but I haven’t encountered any issues yet!\nUsing the Google Cloud tools While I could write a manual exporter/importer and run it as a scheduled Cloud Function, I prefer existing official solutions because I might miss things that Google hopefully doesn’t.\nSo I went with the official Firestore backup guide. Here is a summary of what you need to do:\nInstall Google Cloud SDK or Firebase CLI.\nI will give you examples for both the Google Cloud CLI (that comes with the Google Cloud SDK) and the Firebase CLI.\nLog in.\nExecute gcloud auth login or firebase login. This grants access to your Firebase project.\nChoose your project.\nRun the command gclound config set project PROJECT-ID or firebase use PROJECT-ID. If you don’t know your project id, run the command gcloud projects list or firebase projects:list to get an overview.\nAcquire needed roles\nIf you are an administrator, there is nothing to do here. But if not, you might require additional roles to be able to work with backups. Click HERE for a description of all required roles. And read THIS to assign roles with the Google Cloud CLI.\nCreate a scheduled backup\nRun the command gcloud alpha firestore backups schedules create or firebase firestore:backups:schedules:create You might need to install the alpha components for the Google Cloud CLI (just follow the instructions) since this feature currently is only in a preview state. You can find your database name in the Firebase Console. Make sure to type the correct name including the brackets for the default database and surround it by quotes.\n💡 Tip\nYou can create up to one daily backup and one weekly backup per database!\nHere are some important parameters to configure your backup properly:\nrecurrence - you can choose between daily and weekly retention-period - you can choose any time period between 1w and 14w day-of-week - only available for weekly backups. Choose the day (MON - SUN for Google Cloud CLI, MONDAY - SUNDAY for Firebase CLI) you want the backup to run. Here are example commands\ngcloud alpha firestore backups schedules create \\ --database=\u0026#39;(default)\u0026#39; \\ --recurrence=weekly \\ --retention=7w \\ --day-of-week=SUN firebase firestore:backups:schedules:create \\ --database \u0026#39;(default)\u0026#39; \\ --recurrence \u0026#39;WEEKLY\u0026#39; \\ --retention 7w \\ --day-of-week SUNDAY 💡 Tip\nYou cannot specify exactly when you want your backup to run. For weekly backups, you can specify the day but you have no control about the time.\nHow can I list all backup schedules? To get a list of all backup schedules, use the following command:\ngcloud alpha firestore backups schedules list \\ --database=\u0026#39;(default)\u0026#39; firebase firestore:backups:schedules:list \\ --database \u0026#39;(default)\u0026#39; How can I delete a backup schedule? Deleting a backup schedule can be achieved with this command:\ngcloud alpha firestore backups schedules delete \\ --database=\u0026#39;(default)\u0026#39; \\ --backup-schedule=BACKUP-SCHEDULE-ID firebase firestore:backups:schedules:delete BACKUP-SCHEDULE You can find the BACKUP-SCHEDULE-ID and the BACKUP-SCHEDULE by listing all backup schedules (see previous section).\nYou only delete the scheduler so that no more backups will be created in the future. All existing backups will not be deleted until their retention period expires. Click HERE if you want to delete backups manually.\nHow can I list all created backups? To list all backups, use this command:\ngcloud alpha firestore backups list firebase firestore:backups:list It will show you all backups, when they were created, for what database they are, and when they expire. Here is an example output\ndatabase: projects/xxx/databases/(default) databaseUid: 39d9a097-5662-4a27-b1e2-054c4f9ad486 expireTime: \u0026#39;2024-05-13T22:33:31.540559Z\u0026#39; name: projects/xxx/locations/eur3/backups/yyy snapshotTime: \u0026#39;2024-05-06T22:33:31.461258Z\u0026#39; state: READY The name value is especially useful when you want to restore a backup (see next section).\nHow can I restore my database from a backup? You cannot fill an existing database from a backup. Instead, you pass a database ID to the restore operation. The database will then be created and filled with the data from the backup. It is up to you how you proceed from that point.\nHere is an example command to copy the content of an existing backup into a new database:\ngcloud alpha firestore databases restore \\ --source-backup=projects/PROJECT-ID/locations/LOCATION-ID/backups/BACKUP-ID \\ --destination-database=YOUR-NEW-DATABASE-NAME firebase firestore:databases:restore \\ --backup \u0026#39;BACKUP\u0026#39; \\ --database \u0026#39;YOUR-NEW-DATABASE-NAME\u0026#39; You can find PROJECT-ID, LOCATION-ID, BACKUP-ID, and BACKUP easily by listing all backups (see section above). Name your new database and you are ready to go.\nSince this process takes quite a while, you can inspect the progress with the command (this isn’t possible with the FirebaseCLI)\ngcloud firestore operations list --database=YOUR-NEW-DATABASE-NAME The output contains a progress report like this\n... operationState: PROCESSING progressPercentage: completedWork: \u0026#39;30\u0026#39; estimatedWork: \u0026#39;100\u0026#39; startTime: \u0026#39;2024-05-07T18:12:15.025456Z\u0026#39; ... Don’t expect this to be fast. It can take up to an hour even for small databases!\nEventually, you will end up with an additional database in your project that contains the backup data.\n💡 Tip\nSecurity rules are not part of the backup!\nWhat about pricing? How much does it cost? The service is not for free. You have to be on the Blaze pricing plan and you are charged for storage space and restore operations. The bigger your database is and the higher your retention rate is, the higher your monthly bill will be.\nBut if you backup your default database, you still benefit from the free quota while all other databases in your project don’t.\nTo give you a glimpse of what you can expect:\nThe current storage price per GiB in the US multi-region (typically the or one of the most expensive regions) is $0.03. The current restore operation cost per GiB in the same region is $0.40. Cloud Firestore backup and restore costs in the US multi-region\nClick HERE for a price overview and details about Firebase pricing.\nConclusion In this article, I showed you how to backup your Cloud Firestore database. The Firebase backup solution might be perfect in terms of restoring backups. But it provides an easy way to add recurring data copies with little setup effort. A simple safety net that can save you a lot of trouble. It takes you less than 30 minutes to read this article and set up a backup schedule and it’s worth the effort!\n","permalink":"https://quickcoder.org/posts/how-to-backup-your-cloud-firestore-database/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eBackups are underrated … until you really need them. So here is how to backup your Cloud Firestore database. Forget about external tools or self-written Cloud Functions and use the provided tools instead!\u003c/p\u003e\n\u003cp\u003eI store quite a lot of stuff in Firestore, mostly for personal use. In the last months, I often thought about backups. But I have to admit … I am lazy when it comes to boring stuff. However, some of the data is really valuable and will be lost in case of a disaster. So I finally stopped doing cooler things and investigate backup solutions. Here is my approach on how to backup your Cloud Firestore database.\u003c/p\u003e","title":"How To Backup Your Cloud Firestore Database"},{"content":"Introduction Default, named, constant, factory?!? Here is a quick introduction to different Dart constructors. Knowing the differences can make your code way more readable!\nEverybody needs to use constructors. And everybody needs to know what\u0026rsquo;s possible with them. So here is a quick introduction to different Dart constructors.\nDefault Constructor The default constructor is the constructor type you probably use the most. It creates an instance of an object and is essential in any object-oriented programming language. Here is a small code example:\nclass MyClass { MyClass() { print(\u0026#34;default constructor\u0026#34;); } } // call example final myObject = MyClass(); While many other programming languages require a keyword like new, Dart got rid of that since v2.0.\nNamed Constructor Named constructors are perfect to create objects for specific purposes or give developers the ability to create objects in multiple ways. Have a look at the DateTime class of Dart. It has a default constructor and 5 named constructors (fromMicrosecondsSinceEpoch, fromMillisecondsSinceEpoch, now, timestamp, utc).\nThe developers have the freedom to use whatever constructor matches their preferred style and available data. Here is a code example for named constructors:\nclass MyClass { MyClass.anotherConstructor() { print(\u0026#34;named constructor\u0026#34;); } } // call example final myObject = MyClass.anotherConstructor(); You don’t need to define a default constructor because if you omit it, it is created automatically.\nConstant Constructor A class that only contains unchangeable values (const and final), can declare a const constructor. The Dart compiler can optimize this code and make your app faster. Have a look at this example below:\nclass MyClass { final int id; const MyClass(this.id); } // call example final myObject = const MyClass(5); Consider using this constructor type as often as possible to speed up your final product! It is also possible to combine the const keyword with named constructors.\n💡 Tip\nA constant constructor cannot have a body!\nPrivate Constructor A private constructor is not publicly accessible but only from within the class itself or the Dart file it is defined in. Usually you will have other types of constructors that expose it to the public. Let’s have a look at the code:\nclass MyClass { MyClass._() { print(\u0026#34;private constructor\u0026#34;); } } // call example // Only works when called from within the same file! final myObject = MyClass._(); You can also name your private constructor as long as it starts with an underscore.\nFactory Constructor A factory constructor is useful when you don’t always create a new object. The Singleton pattern is the perfect example for this type of constructor. Here is the code:\nclass MyClass { static MyClass _instance; MyClass._(); factory MyClass() { print(\u0026#34;factory constructor\u0026#34;); if (_instance == null) { _instance = MyClass._(); } return _instance; } } // call example final myObject = MyClass(); final myNewObject = MyClass(); print(myObject == myNewObject) // returns true You can also combine the factory keyword and a named constructor.\n💡 Tip\nFactory constructors can’t access the this context.\nRedirecting Constructor A redirecting constructor redirects the call flow to another constructor. This can be any constructor type except factory constructors. You can also chain redirecting calls as much as you want. Here is a quick example of a named constructor that redirects to the default constructor:\nclass MyClass { String name; int age; MyClass(this.name, this.age); MyClass.example() : this(\u0026#34;Rahul\u0026#34;, 35) } // call example final myObject = MyClass.example(); Use the this context to access other constructors and instance fields in a redirecting constructor.\n💡 Tip\nA redirecting constructor cannot have a body!\nSuper Constructor A super constructor looks similar to a redirecting constructor but it calls constructors in the base class. All other possibilites of redirecting constructors are also available here like chaining calls or passing arguments. Again some code:\nclass MyClass { String name; int age; MyClass(this.name, this.age); } class AnotherClass extends MyClass { String country; AnotherClass(super.name, super.age, this.country); // or AnotherClass(String name, int age, this.country) : super(name, age); } // call example final myObject = AnotherClass(\u0026#34;Rahul\u0026#34;, 35, \u0026#34;India\u0026#34;); While a redirecting constructor can’t have a method body, a super constructor can! And there are currently two synatic options to call a super constructor with arguments. Both lead to the same result.\nRemarks From other programming languages, you might know constructor overloading. This means defining multiple constructors with the same name but different parameters. Here is an example with C#:\nclass Person { public string Name { get; set; } public int Age { get; set; } // Default constructor public Person() { Name = \u0026#34;Unknown\u0026#34;; Age = 0; } // Constructor with one parameter public Person(string name) { Name = name; Age = 0; } // Constructor with two parameters public Person(string name, int age) { Name = name; Age = age; } } This is not possible with Dart. But you can shift around that problem by defining multiple named constructors and redirecting them to the default constructor, for example. Here is what I mean:\nclass Person { String name; int age; Person(this.name, this.age); Person.empty() : this(\u0026#34;Unknown\u0026#34;, 0); Person.nameOnly(String myName) : this(myName, 0); Person.ageOnly(int myAge) : this(\u0026#34;Unknown\u0026#34;, myAge); Person.nameAndAge(String myName, int myAge) : this(myName, myAge); } Conclusion In this article, I gave you a quick introduction to different Dart constructor types. With this guide you should know the differences and when to use what, no matter if you need default, named, const, factory, or super constructors.\n","permalink":"https://quickcoder.org/posts/a-quick-introduction-to-different-dart-constructors/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eDefault, named, constant, factory?!? Here is a quick introduction to different Dart constructors. Knowing the differences can make your code way more readable!\u003c/p\u003e\n\u003cp\u003eEverybody needs to use constructors. And everybody needs to know what\u0026rsquo;s possible with them. So here is a quick introduction to different Dart constructors.\u003c/p\u003e\n\u003ch1 id=\"default-constructor\"\u003eDefault Constructor\u003c/h1\u003e\n\u003cp\u003eThe default constructor is the constructor type you probably use the most. It creates an instance of an object and is essential in any object-oriented programming language. Here is a small code example:\u003c/p\u003e","title":"A Quick Introduction To Different Dart Constructors"},{"content":"Introduction In this article, I guide you through the setup steps for Cloud Firestore and show you how to use Firebase Cloud Firestore with a Flutter app. We take a look at CRUD operations, security rules, and some pitfalls to avoid.\nFirebase Cloud Firestore is a scalable NoSQL cloud database where you can store relevant data for your apps. It is very flexible and can be secured with access rules. You will learn ow to use Firebase Cloud Firestore with a Flutter app. In the following sections, we’ll talk about\nSetting up the database Working with the Firebase database editor Accessing the database from a Flutter application Adding security rules for access control You will need to set up a Firebase project by yourself to use these code examples here or the one from my GitHub page. If you haven’t done that already, here is an article to guide you through the necessary steps.\nInitial setup Open the Firestore Database menu in your Firebase dashboard.\nScreenshot of Firestore Database menu entry by author\nNext, create a new database by clicking the button Create database.\nScreenshot before creating a Cloud Firestore new database by author\nThe following wizard screen asks for production or test mode. We’ll start with test mode and configure security rules later in the process. Be aware that without security rules, anyone can read and write (and even delete) data in your database!\nScreenshot of mode selection for Cloud Firestore by author\nFinally, you’ll be asked to select a location. This affects pricing and availability (you can read further details here). For this example, there won’t be any costs as the database contains enough free operations.\nScreenshot of location selection for Cloud Firestore by author\nAfter the process, you’ll see an empty database ready to use.\nScreenshot of empty Cloud Firestore database by author\nIn the next steps, we’ll add, edit, and delete data, and configure security rules to prevent third-party access.\nWorking with the Firebase editor The Cloud Firestore editor allows you to add, edit, and delete data from the web dashboard.\nClick on + Start collection to create a new one. A collection is basically a folder that contains multiple documents. We will name it cars in this example.\nScreenshot of collection creation by author\nIn the next step, you’ll be asked to add the first document. This can be anything from just one simple key-value pair to complex and nested JSON objects. You can even start new collections in a document. For identification, every document needs an ID which can either be auto-created by Firebase (my recommendation for now) or you take care of it. Hit the Auto-ID button to generate an ID for your document.\nScreenshot of document creation by author\nAs you can see, I added some fields, types, and values to demonstrate the editor. We won’t be using the graphical editor, but focus on managing data with code in the next session.\nAccess from a Flutter application The first step is to install the cloud_firestore package. After that, you can access the database. The following code examples show how to get, add, edit, and delete data with Cloud Firestore.\nThe workflow is pretty similar in all methods. Use the FirebaseFirestore.instance, access a Collection identified by a string, use orderBy or where clauses, and perform an asynchronous get. Take the docs property to get a List\u0026lt;QueryDocumentSnapshot\u0026lt;Map\u0026lt;String, dynamic\u0026gt;\u0026gt;\u0026gt;. Every single list item can be converted into a JSON Map with the data() method. And finally, you can convert the Map into a real dart object.\n💡Tip\nUse tools like dart-quicktype to generate class definitions from JSON code.\nTo add documents (entries) with auto-generated IDs, call the add() method on the desired collection. The collection will be created if it cannot be found. If you want to specify the document ID, use the collection(”someCollection”).doc(”myUniqueDocId”).set(data) approach.\n❗Difference between set and update\nLet’s assume, your data structure in a Firebase document looks like this:\n{ “text”: “Hello World”, “number”: 3 }\nWhen performing an update operation with { “text”: “World Hello” }, only the text property is changed. Nothing happens to the number property. When performing a set operation with { “text”: “World Hello” }, the text property will be changed, but the number property will be removed!\nBe careful when using set()!\nCheck out the source code for a complete example of how to use the basic database operations.\nHere is a short demo video of the application. It inserts data into the database, performs two get operations with sorting and filtering, and finally deletes the data again.\nCloud Firestore demo application by author\nSet up security rules Security rules ensure that only those operations happen on the data that are supposed to happen. There are various options to configure rules, but I’ll give only a brief introduction. For further details, refer to the Firebase documentation or the following two articles.\nBasic examples of using Cloud Firestore Security Rules Advanced examples of using Cloud Firestore Security Rules Your current rule will look similar to this:\nservice cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if request.time \u0026lt; timestamp.date(2022, 6, 22); } } } The first line identifies the used service because rules can also apply to Cloud Storage and Realtime Database. The following match statement(s) identify for which entities the rule should be enforced. You can nest multiple match statements or just use one. The allow statement tells us under which condition(s) access is allowed. After the allow keyword, you can add multiple methods like read and write. The following methods are available:\nget - read single documents list - read queries create - write new documents update - write existing documents delete - delete data read - get + list write - create + update match statements often use wildcards. Text in curly brackets like {database} is a single-segment wildcard and can be used in conditions, for example. To make the wildcard recursive, add two star characters {document=**}.\nThe rule above denies access to all documents in the database after June, 22nd of 2022.\nHere are some common rule conditions that you can use:\nallow read, write: if false; // no access allow read, write: if request.auth != null; // access when authenticated match /users/{userId} { allow read, write: if request.auth != null \u0026amp;\u0026amp; request.auth.uid == userId; } // access only own documents match ... { allow read: if true; // read access for everyone allow write: if request.auth != null; // write access when authenticated } You can find more examples in the articles above and in the Firebase documentation.\nConclusion In this article, I showed you how to use Firebase Cloud Firestore with a Flutter app. Cloud Firestore is a simple cloud database solution for your app. With the provided examples and source code you should be able to make your first steps.\nYou can find the complete example source code on my GitHub page.\n","permalink":"https://quickcoder.org/posts/firebase-firestore/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eIn this article, I guide you through the setup steps for Cloud Firestore and show you how to use Firebase Cloud Firestore with a Flutter app. We take a look at CRUD operations, security rules, and some pitfalls to avoid.\u003c/p\u003e\n\u003cp\u003eFirebase Cloud Firestore is a scalable NoSQL cloud database where you can store relevant data for your apps. It is very flexible and can be secured with access rules. You will learn ow to use Firebase Cloud Firestore with a Flutter app. In the following sections, we’ll talk about\u003c/p\u003e","title":"How To Use Firebase Cloud Firestore With A Flutter App"},{"content":"Introduction This short article shows how to perform HTTP requests from a Flutter application. We have a look at the most common request types GET, POST, PUT, and DELETE.\nPerforming HTTP requests is a must-have for every app nowadays. In this article, I am going to show you how to add this capability to your Flutter app. We will have a look at two packages to accomplish this task: The http package and the dio package. They are very similar and you can use whatever you like more. In the end, we focus on parsing JSON results from requests, so that the data can be used within our app.\nHTTP package Using the http package is pretty simple. Import it in your file by adding\nimport 'package:http/http.dart' at the top. Then you get access to all the required http functions. See the usage examples below.\nYou can see examples for the methods GET, POST, PUT, and DELETE. These are the most common ones, so we’ll focus on them.\nEvery call returns a Response object with details about the requests’ execution. It contains properties like statusCode, contentLength, or reasonPhrase in case of failures. Details about the status codes will be discussed in the coming section.\nresponse.body contains a string with the return values of the request. To make a Dart Map\u0026lt;String, dynamic\u0026gt; from it, use the jsonDecode function.\nDIO package Dio is pretty similar to the http package. First, import it with import 'package:dio/dio.dart'. Then you can use it like this:\nEvery call returns a Response object with details about the requests’ execution. It contains properties like statusCode, headers, or statusMessage in case of failures. Details about the status codes will be discussed in the coming section.\nresponse.data contains a dynamic object with the return values of the request. To make a Dart Map\u0026lt;String, dynamic\u0026gt; from it, you just need to cast it to that type with the as operator.\nHTTP status codes To verify the correct execution of a request, you can evaluate the status code of the returned response. This gives you a good hint (and sometimes even the exact error) when debugging apps. A complete list of all status codes can be found at the Mozilla Developer Network. I’ll introduce the most common ones.\nSimple rule: 200–299 ✅, everything else ❌\n200 OK\nThe request succeeded. Any method can return this code.\n201 Created\nThe request succeeded and a new resource was created. Typically returned by a POST or PUT request.\n204 No Content\nThe request returned no data but indicates successful execution.\n400 Bad Request The server cannot process the client data. This happens mostly if the data is sent in the wrong format or required data is missing. Check the error message for details.\n401 Unauthorized\nThe client needs to authenticate itself, before the server processes the request. Maybe a header field with an identifier is missing in the request.\n403 Forbidden\nThe client does not have the right to execute this operation.\n404 Not Found\nThe requested resource cannot be found by the server.\n500 Internal Server Error\nA situation occurred, that the server can’t handle. This happens usually due to programming errors on the server-side.\nParse JSON result If you want to work with custom objects instead of Map\u0026lt;String, dynamic\u0026gt;, you can convert the JSON in a fast and simple way. Let’s assume, the queried API returns the following JSON:\nThe class, you need to create to map the data successfully, looks like this:\nThis can be very nasty and boring and of course, there are more complex scenarios like lists of objects or nested objects. If you want to dive deep into the parsing topic, check out this article which explains everything in detail.\n🔔 Hint\nHowever, the quick way is to use dart quicktype. It’s an online tool that automatically creates data classes from JSON input. Just paste the JSON result into the editor, select Dart as language, and copy the created code. No need to write data classes by hand!\nConclusion With this guide, you should be able to perform HTTP requests from your Flutter app and parse the response data.\n","permalink":"https://quickcoder.org/posts/flutter-http-requests/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eThis short article shows how to perform HTTP requests from a Flutter application. We have a look at the most common request types GET, POST, PUT, and DELETE.\u003c/p\u003e\n\u003cp\u003ePerforming HTTP requests is a must-have for every app nowadays. In this article, I am going to show you how to add this capability to your Flutter app. We will have a look at two packages to accomplish this task: The \u003ca href=\"https://pub.dev/packages/http\"\u003ehttp\u003c/a\u003e package and the \u003ca href=\"https://pub.dev/packages/dio\"\u003edio\u003c/a\u003e package. They are very similar and you can use whatever you like more. In the end, we focus on parsing JSON results from requests, so that the data can be used within our app.\u003c/p\u003e","title":"How To Make HTTP Requests With Flutter And Parse JSON Result Data"},{"content":"Introduction Learn how I created an Instagram clone with Flutter and Firebase. Fun fact: Creating the UI took the most time! However, it was really easy overall. Here is a deep dive for you!\nInstagram is a well-known app with hundreds of millions of users. The newsfeed with posts, likes, comments, and images is the main feature that stands out. I wanted to know what it takes to build a small app with those features. So here is how I created an Instagram clone with Flutter and Firebase.\nVideo of my Instagram clone app built with Flutter and Firebase\nTools First, let’s talk about everything that I used during development. It’s not that much to be honest. And that’s good because simple things are easier to maintain and to enhance. I always head for simple solutions.\nI use Firebase as my backend. The major benefit is that this cloud service doesn’t require any maintenance or updates. It just works out of the box. In addition, it has a generous free tier that makes developing and testing cheap. It’s always my first choice when I need to create a MVP.\nFirebase Firestore is a document-based NoSQL database that I used to store all posts. Every post is a document. With the provided query language, it’s easy to get exactly the data you want. I published an introduction article about Firebase Firestore for Flutter developers if you want to know more.\nFirebase Storage is a cloud storage service for all your files. No matter if it’s audio, video, PDF, ZIPs, or else, Cloud Storage can handle it. When users upload images in posts, they are stored here. Read my introduction article about it here.\nRiverpod is my favorite state management tool. It took me a while to get the hang of it but I don’t think that there is a better solution currently available. It’s the glu in my app that connects models, services, and widgets.\nApart from that I use only a few other packages:\ntimeago for displaying nice time strings like “5 minutes ago”\nequatable to compare object instances\nAn image picker to select and upload pictures from a device\nThese are my essentials for building an Instagram clone!\nGoal Of course, I don’t have the time (and probably not the knowledge and capabilities) to build an exact clone. That’s why I focused on those main features that my app should fulfill:\nCreate posts with text and images\nUsers can enter text and upload an optional image.\nLike feature\nLiking and unliking of posts should be possible. No dislike feature, although it would have a similar logic. Comments cannot be liked.\nComment option\nCrucial feature for social networks. A comment can only be text, no image uploading planned.\nAuthor can delete own posts or comments\nMistakes happen. That’s why people should be able to correct them by deleting their stuff again. In addition, one could think about an admin feature that can delete everything from everyone.\nUpdates should be minimal, data should be cached on the device\nFirebase offers 50,000 free read operations per day for Firestore. When there are 100 posts and 50 users and every user refreshes 10 times a day, the quota is depleted. That’s why I aim for caching and intelligent updates.\nAuthentication mechanism\nUsers should be able to register, log in, reset passwords, and all the usual stuff that you know. This will be realized with Firebase Authentication.\nProfile pictures\nAll users can upload a profile picture that is displayed alongside their posts and comments. It will be stored in Firebase Storage.\nThe data model I usually start with the data model since I am working alone on the project. In team projects, it’s better to define the interfaces first so that all developers can start on different tasks and integrate later.\nI am going with two collections in Firebase Firestore for all posts and users. The reference between the two collections is the Firebase Authentication UID. It would also be possible to only go with a posts collection and store all information in there. But I want a bit more separation between those entities and that’s why I went with 2 collections. However, the Guest object is included and also updated in every post, like, or comment when the data changes.\nCode for a Post object import \u0026#39;package:cloud_firestore/cloud_firestore.dart\u0026#39;; import \u0026#39;package:equatable/equatable.dart\u0026#39;; import \u0026#39;package:hochzeit/models/guest.dart\u0026#39;; import \u0026#39;package:hochzeit/models/post_comment.dart\u0026#39;; class Post extends Equatable { final String id; final String text; final String imageId; final Guest author; final DateTime createdAt; final DateTime updatedAt; final List\u0026lt;PostComment\u0026gt; comments; final List\u0026lt;Guest\u0026gt; likes; final bool isDeleted; const Post( {required this.id, required this.text, required this.author, required this.createdAt, required this.updatedAt, required this.comments, required this.likes, required this.isDeleted, required this.imageId}); Post update() =\u0026gt; Post( id: id, createdAt: createdAt, text: text, author: author, comments: comments, likes: likes, imageId: imageId, isDeleted: isDeleted, updatedAt: DateTime.now()); factory Post.fromJson(Map\u0026lt;String, dynamic\u0026gt; json) { return Post( id: json.containsKey(\u0026#34;id\u0026#34;) ? json[\u0026#34;id\u0026#34;] : \u0026#34;\u0026#34;, text: json.containsKey(\u0026#34;text\u0026#34;) ? json[\u0026#34;text\u0026#34;] : \u0026#34;\u0026#34;, createdAt: json.containsKey(\u0026#34;createdAt\u0026#34;) ? (json[\u0026#34;createdAt\u0026#34;] as Timestamp).toDate() : DateTime(1970), updatedAt: json.containsKey(\u0026#34;updatedAt\u0026#34;) ? (json[\u0026#34;updatedAt\u0026#34;] as Timestamp).toDate() : DateTime(1970), author: json.containsKey(\u0026#34;author\u0026#34;) ? Guest.fromJson(json[\u0026#34;author\u0026#34;]) : Guest.empty(), imageId: json.containsKey(\u0026#34;imageId\u0026#34;) ? json[\u0026#34;imageId\u0026#34;] : \u0026#34;\u0026#34;, isDeleted: json[\u0026#34;isDeleted\u0026#34;], comments: json.containsKey(\u0026#34;comments\u0026#34;) ? (json[\u0026#34;comments\u0026#34;] as List) .map((item) =\u0026gt; PostComment.fromJson(item)) .toList() : \u0026lt;PostComment\u0026gt;[], likes: json.containsKey(\u0026#34;likes\u0026#34;) ? (json[\u0026#34;likes\u0026#34;] as List) .map((item) =\u0026gt; Guest.fromJson(item)) .toList() : \u0026lt;Guest\u0026gt;[]); } Map\u0026lt;String, dynamic\u0026gt; toJson() { return \u0026lt;String, dynamic\u0026gt;{ \u0026#34;id\u0026#34;: id, \u0026#34;text\u0026#34;: text, \u0026#34;createdAt\u0026#34;: Timestamp.fromDate(createdAt), \u0026#34;updatedAt\u0026#34;: Timestamp.fromDate(updatedAt), \u0026#34;author\u0026#34;: author.toJson(), \u0026#34;imageId\u0026#34;: imageId, \u0026#34;isDeleted\u0026#34;: isDeleted, \u0026#34;comments\u0026#34;: comments .map((comment) =\u0026gt; comment.toJson()) .toList(), \u0026#34;likes\u0026#34;: likes.map((like) =\u0026gt; like.toJson()).toList() }; } @override List\u0026lt;Object?\u0026gt; get props =\u0026gt; [id]; } Code for a Guest object import \u0026#39;package:equatable/equatable.dart\u0026#39;; class Guest extends Equatable { final String id; final String uid; final String firstName; final String lastName; final String displayName; final String profilePicture; const Guest( {required this.id, required this.uid, required this.firstName, required this.lastName, required this.displayName, required this.profilePicture}); Guest.empty() : this( id: \u0026#34;\u0026#34;, uid: \u0026#34;\u0026#34;, firstName: \u0026#34;\u0026#34;, lastName: \u0026#34;\u0026#34;, displayName: \u0026#34;\u0026#34;, profilePicture: \u0026#34;\u0026#34;); factory Guest.fromJson(Map\u0026lt;String, dynamic\u0026gt; json) { return Guest( id: json.containsKey(\u0026#34;id\u0026#34;) ? json[\u0026#34;id\u0026#34;] : \u0026#34;\u0026#34;, uid: json.containsKey(\u0026#34;uid\u0026#34;) ? json[\u0026#34;uid\u0026#34;] : \u0026#34;\u0026#34;, firstName: json.containsKey(\u0026#34;firstName\u0026#34;) ? json[\u0026#34;firstName\u0026#34;] : \u0026#34;\u0026#34;, lastName: json.containsKey(\u0026#34;lastName\u0026#34;) ? json[\u0026#34;lastName\u0026#34;] : \u0026#34;\u0026#34;, displayName: json.containsKey(\u0026#34;displayName\u0026#34;) ? json[\u0026#34;displayName\u0026#34;] : \u0026#34;\u0026#34;, profilePicture: json.containsKey(\u0026#34;profilePicture\u0026#34;) ? json[\u0026#34;profilePicture\u0026#34;] : \u0026#34;\u0026#34;); } Map\u0026lt;String, dynamic\u0026gt; toJson() { return \u0026lt;String, dynamic\u0026gt;{ \u0026#34;id\u0026#34;: id, \u0026#34;uid\u0026#34;: uid, \u0026#34;firstName\u0026#34;: firstName, \u0026#34;lastName\u0026#34;: lastName, \u0026#34;displayName\u0026#34;: displayName, \u0026#34;profilePicture\u0026#34;: profilePicture, }; } @override List\u0026lt;Object?\u0026gt; get props =\u0026gt; [id, uid]; } These models contain all the information that we need to achieve the requirements discussed before. As you can see, the classes use equatable to compare instances via the unique id. The package saves me some boilerplate code. There are also fromJson and toJson methods so that I can read and write data easily with Firebase Firestore.\nAuthentication My demo application of how I created an Instagram clone with Flutter and Firebase uses email/password authentication. Users enter their email and password on a login screen and are redirected to their news feed. This is pretty simple with the package firebase_auth.\nI omitted features like registration and password reset. But they are not complex to implement. To register a new user, use the method createUserWithEmailAndPassword and that’s it. And for resetting a password, use the methods sendPasswordResetEmail, verifyPasswordResetCode, and confirmPasswordReset. You can also define the email templates to communicate with your users in Firebase.\nNews feed To keep it simple, we order the news feed chronogically. New stuff is at the top and older content at the bottom.\nShow all posts Firebase Cloud Firestore offers Streams of collections that can be easily integrated in your Flutter app. First, let’s define the Stream we want to subscribe to. In this example, we want all posts that are not deleted and have them ordered from new to old.\nStream\u0026lt;QuerySnapshot\u0026gt; _getPostsStream() { return FirebaseFirestore.instance .collection(\u0026#39;posts\u0026#39;) .where(\u0026#34;isDeleted\u0026#34;, isNotEqualTo: true) .orderBy(\u0026#34;isDeleted\u0026#34;, descending: true) .orderBy(\u0026#34;createdAt\u0026#34;, descending: true) .snapshots(); } To display the data, we use a StreamBuilder. Here is how it could look like:\nStreamBuilder( stream: _getPostsStream(), builder: (context, snapshot) { if (snapshot.hasError) { // handle error } if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } List\u0026lt;Widget\u0026gt; postCards = []; for (int index = 0; index \u0026lt; snapshot.data!.docs.length; index++) { final post = Post.fromJson(snapshot.data!.docs[index] .data() as Map\u0026lt;String, dynamic\u0026gt;); posts.add(post); postCards.add(PostCard( item: post, )); } postCards.add(_buildLoadingButton(snapshot.data)); return ListView( padding: const EdgeInsets.fromLTRB(0, 0, 0, 80), physics: const AlwaysScrollableScrollPhysics(), children: postCards); }) By the way: Firebase Cloud Firestore automatically caches data if you are on iOS or Android. For Flutter web, you need to enable it manually. This can help you reduce read operations and also save costs. Read this article for details:\nPost new content A post can either contain text and an image or only text. Users enter the content and publish it. The new post is added to the database, the optional image is uploaded to the cloud, and the news feed updates to reflect the changes.\nHere is the user interface:\nUser interface screenshot of my Instagram clone with Flutter and Firebase\nOk, let’s start with the image. There is a nice package called image_picker which allows selecting an image from the camera or the gallery. The following code handles a selected image:\nfinal image = await ImagePicker().pickImage(source: ImageSource.gallery); if (image != null) { final imageFile = await image.readAsBytes(); ... } Eventually, the user will publish the post. Then, we need to upload the image to get the url, create a new Post object, and insert it into the posts collection in Firestore. Here is how you can do exactly that:\n// select an image final image = await ImagePicker().pickImage(source: source); if (image != null) { final imageFile = await image.readAsBytes(); // upload the image final task = uploadFile(imageFile, image.name); task.snapshotEvents.listen((event) async { if (event.state == TaskState.success) { // get the download url of the uploaded image _imagePath = await event.ref.getDownloadURL(); } else if (event.state == TaskState.error) { // Handle error } }); } // upload an image to Firebase Storage // you need to pass in the file content and the name like \u0026#34;myImage.jpg\u0026#34; UploadTask uploadFile(Uint8List file, String fileName) { final ref = FirebaseStorage.instance.ref(); final child = ref.child(\u0026#34;images/$fileName\u0026#34;); return child.putData(file); } // add a new post to a Firebase Firestore collection Future\u0026lt;Post\u0026gt; addPost(String text, String imageId, Guest author) async { final docRef = FirebaseFirestore.instance.collection(\u0026#34;posts\u0026#34;).doc(); final newPost = Post( id: docRef.id, author: author, comments: const \u0026lt;PostComment\u0026gt;[], likes: const \u0026lt;Guest\u0026gt;[], updatedAt: DateTime.now(), createdAt: DateTime.now(), text: text, isDeleted: false, imageId: imageId); await FirebaseFirestore.instance .collection(\u0026#34;posts\u0026#34;) .doc(docRef.id) .set(newPost.toJson()); return newPost; } With this implementation, users can add exactly one image. However, image_picker supports multi-select in case you want more. And if you want other file types than images, try file_picker.\nAdd a comment A PostComment can be treated similar to a Post, but in my case only text is possible. Once a user created a comment, it is added to the comments list of the corresponding Post object. The last step is to update Firestore and replace the old Post with the new one.\nFuture addComment(Post post, PostComment comment) async { post.comments.add(comment); // this creates a new copy with the last updated timestamp set to now final postCopy = post.update(); await FirebaseFirestore.instance .collection(\u0026#34;posts\u0026#34;) .doc(postCopy.id) .update(postCopy.toJson()); } Like a post Every Post contains a list of Likes which is just a reference to a user. When a user clicks on the like button in the app, then its ID is added to the list. Once again, we update the Post object in the database and the news feed will show the updated information.\nFuture likePost(Post post, Guest guest) async { post.likes.add(guest); // this creates a new copy with the last updated timestamp set to now final postCopy = post.update(); await FirebaseFirestore.instance .collection(\u0026#34;posts\u0026#34;) .doc(postCopy.id) .update(postCopy.toJson()); } You can also implement a toggle so that users can unlike again when they liked something before.\nDelete your content Deleting does not mean the content is gone, it just isn’t visible anymore. This can be done with a boolean flag isDeleted and content with that property is excluded from the news feed.\nWhy? Because then you can restore the content in case a user deleted by accident.\nFuture deletePost(Post post) async { final newPost = Post( id: post.id, author: post.author, comments: post.comments, likes: post.likes, updatedAt: DateTime.now(), createdAt: post.createdAt, text: post.text, isDeleted: true, // \u0026lt;-- here is the relevant flag imageId: post.imageId); await FirebaseFirestore.instance .collection(\u0026#34;posts\u0026#34;) .doc(post.id) .set(newPost.toJson()); } In case you really want to delete stuff, there is a delete method for documents in Cloud Firestore.\nProfile pictures We already talked about images in posts in a previous section of this article about how I created an Instagram clone with Flutter and Firebase. Profile pictures follow the same pattern.\nI build a UI for the user to select a profile picture, to change it, and to remove it using the image_picker package and Firebase Storage. It is very similar to what you have been before. But here is the catch:\nSince we store the author information with every post, we need to update every post and include the author image. Otherwise, the image won’t be displayed!\n// find all posts where the user is author, has liked, or has commented // then we update the author information, the like information, or the comment // information if our user matches Future updateGuestInPosts( List\u0026lt;Post\u0026gt; allPosts, Guest oldGuest, Guest newGuest) async { final postsToUpdate = allPosts .where((post) =\u0026gt; post.author == oldGuest || post.comments.any((comment) =\u0026gt; comment.author == oldGuest) || post.likes.any((like) =\u0026gt; like == oldGuest)) .toList(); for (var post in postsToUpdate) { final newPost = Post( id: post.id, text: post.text, author: post.author == oldGuest ? newGuest : oldGuest, createdAt: post.createdAt, updatedAt: DateTime.now(), isDeleted: post.isDeleted, imageId: post.imageId, likes: _replaceLikes(post, oldGuest, newGuest), comments: _replaceComments(post.comments, oldGuest, newGuest)); await FirebaseFirestore.instance .collection(\u0026#34;posts\u0026#34;) .doc(newPost.id) .set(newPost.toJson()); } } List\u0026lt;Guest\u0026gt; _replaceLikes(Post post, Guest oldGuest, Guest newGuest) { final index = post.likes.indexOf(oldGuest); if (index == -1) return post.likes; post.likes.removeAt(index); post.likes.insert(index, newGuest); return post.likes; } List\u0026lt;PostComment\u0026gt; _replaceComments( List\u0026lt;PostComment\u0026gt; comments, Guest oldGuest, Guest newGuest) { return comments .map((comment) =\u0026gt; PostComment( id: comment.id, text: comment.text, createdAt: comment.createdAt, author: comment.author == oldGuest ? newGuest : oldGuest)) .toList(); } Check out the final result of what you can build with Flutter and Firebase!\nVideo of my Instagram clone app built with Flutter and Firebase\nConclusion In this article, you learned how I created an Instagram clone with Flutter and Firebase. While I spent most of the time with the UI, the backend logic was rather simple. However, there is still room for improvement but I already achieved my goal.\n","permalink":"https://quickcoder.org/posts/how-i-created-an-instagram-clone-with-flutter-and-firebase/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eLearn how I created an Instagram clone with Flutter and Firebase. Fun fact: Creating the UI took the most time! However, it was really easy overall. Here is a deep dive for you!\u003c/p\u003e\n\u003cp\u003eInstagram is a well-known app with hundreds of millions of users. The newsfeed with posts, likes, comments, and images is the main feature that stands out. I wanted to know what it takes to build a small app with those features. So here is how I created an Instagram clone with Flutter and Firebase.\u003c/p\u003e","title":"How I Created An Instagram Clone With Flutter And Firebase"},{"content":"Introduction Follow these 6 easy tips when working with setState in a Flutter application and learn more about the mechanics of setState. Stick to the best practices to not encounter any unwanted errors.\nThe setState function of the StatefulWidget is a simple approach to managing the state within a Flutter app. But there are several pitfalls you need to avoid when you want your app to be working and performant. Here are some best practices you should stick to. So here are 6 easy tips when working with setState in a Flutter application.\nWhat is setState good for? setState is the Flutter way to issue a rebuild of the current widget and its descendants. During a rebuild, the most recent variable values will be used to create the user interface. Let’s say, a user toggles a switch from on to off. The switch has a backing variable that stores that value, so after the change, it is set to false. The switch itself doesn’t reflect that change until it is rebuilt with its new backing field value.\nChange a value Call setState() User interface is updated Tip 1: Keep your widgets small! setState triggers a rebuild of the widget that you are currently in. If your whole application contains only one widget, then this whole widget will be rebuilt which makes your app slow. See the following example:\nHere we have 5 SwitchListTile widgets in a Column and they are all part of the same widget. If you switch any control, the entire screen gets rebuilt. Scaffold, AppBar, Column, \u0026hellip; but it would be enough to just rebuild the widget that has changed. Let’s look at the next code example:\nHere we wrapped the SwitchListTile in a single StatefulWidget. The pages look identical, but if you click on any switch in this example, only the clicked widget will rebuild. You won’t notice any performance enhancements for this small case but on a larger scale, it will make a difference.\nTip 2: Don’t call setState in build methods From the Flutter API documentation\nThis method can potentially be called in every frame and should not have any side effects beyond building a widget.\nThe build method is intended to build the widget tree, so we should keep it that way. Don’t do fancy stuff here, it will slow down your app. Calls to setState will probably trigger additional rebuilds and in the worst case, you might end up having an exception telling you that there is currently a rebuild in progress.\nTip 3: Don’t call setState in initState methods initState will trigger a rebuild after completion so it’s not necessary to call setState inside this method. This method is intended to initialize state-relevant properties like setting default values or subscribing to streams. Don’t do anything else here!\nTip 4: setState() and setState(…) are equal It doesn’t matter if you use setState like this\nsetState((){ _text = “Hello”; }); or like this\n_text = “Hello”; setState((){}); The outcome is the same.\nTip 5: setState(…) code must be small Don’t do any big computations within setState because it will block your app from rebuilding the screen. Look at the following sample code:\nsetState(() { for (var i = 0; i \u0026lt; 10000; i++) print(i); _value = true; }); Only after the print statements, the widget will rebuild. During that time your app doesn’t respond to user actions and it will perform them afterward. So if a user clicks on a control multiple times because there is no visual feedback, multiple rebuilds are stacked up and will slow down the app even more.\nA better approach is to show a progress indicator while performing a long-running operation so that the user knows that something is happening and he needs to wait for the completion.\nTip 6: setState(…) code must not be async When running the code\nsetState(() async { await Future.delayed(const Duration(seconds: 5)); }); you will end up with an exception message like this:\nException when using async code in setState\nPerform your async operations outside of the method and call it afterward.\nConclusion I hope these insights help you better understand the mechanics of setState in Flutter. Stick to these tips and you’ll have fewer problems and faster apps. Source code examples can be found on GitHub.\n","permalink":"https://quickcoder.org/posts/flutter-set-state/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eFollow these 6 easy tips when working with \u003ccode\u003esetState\u003c/code\u003e in a Flutter application and learn more about the mechanics of \u003ccode\u003esetState\u003c/code\u003e. Stick to the best practices to not encounter any unwanted errors.\u003c/p\u003e\n\u003cp\u003eThe \u003ccode\u003esetState\u003c/code\u003e function of the \u003ccode\u003eStatefulWidget\u003c/code\u003e is a simple approach to managing the state within a Flutter app. But there are several pitfalls you need to avoid when you want your app to be working and performant. Here are some best practices you should stick to. So here are 6 easy tips when working with setState in a Flutter application.\u003c/p\u003e","title":"6 Easy Tips When Working With SetState In A Flutter Application"},{"content":"Introduction Get better scrolling and animations, less memory consumption, and more execution speed. Boost your Flutter app performance with these 6 tips. And if you want more, I have a free ebook with additional tips for you at the end!\nHere are some easy-to-use best practices for Flutter apps when it’s all about performance. Use them if you notice any problems but don’t optimize prematurely. Flutter apps are fast by default and you won’t have any trouble usually. Here is how you can boost your Flutter app performance with these 6 tips.\nAlso, make sure to download my free ebook about Flutter performance tips. You can find the link at the end of this article.\nPrefer StatelessWidget over StatefulWidget A StatelessWidget is faster than a StatefulWidget because it doesn’t need to manage state as the name implies. That’s why you should prefer it if possible. Choose a StatefulWidget when…\n▶ you need a preparation function with initState()\n▶ you need to dispose of resources with dispose()\n▶ you need to trigger a widget rebuild with setState()\n▶ your widget has changing variables (non-final) In all other situations, you should prefer a StatelessWidget.\n⛔ Bad example\nimport \u0026#39;package:flutter/widgets.dart\u0026#39;; class MyWidget extends StatefulWidget { final String header; final String subheader; const MyWidget({Key? key, required this.header, required this.subheader}): super(key: key); @override State\u0026lt;MyWidget\u0026gt; createState() =\u0026gt; _MyWidgetState(); } class _MyWidgetState extends State\u0026lt;MyWidget\u0026gt; { @override Widget build(BuildContext context) { return Column(children: [Text(widget.header), Text(widget.subheader)]); } } ✅ Good example\nimport \u0026#39;package:flutter/widgets.dart\u0026#39;; class MyWidget extends StatelessWidget { final String header; final String subheader; const MyWidget({Key? key, required this.header, required this.subheader}): super(key: key); @override Widget build(BuildContext context) { return Column(children: [Text(header), Text(subheader)]); } } Use builder constructors on long lists or grids When dealing with lists or grids with many items, it is recommended to use the builder constructors. Their advantage is that only visible elements are rendered. So if your list or grid is bigger than one device screen, you should choose this approach. For smaller lists and grids, the default constructors can also be used. This applies to all types of lists and grids like ListView, ReorderableListView, or GridView. Always check for a builder constructor and use it if possible.\n✅ Good example\nfinal List\u0026lt;int\u0026gt; _listItems = \u0026lt;int\u0026gt;[1, 2, 3, 4, 5, 6, 7, 8, 9]; @override Widget build(BuildContext context) { return ListView.builder( itemCount: _listItems.length, itemBuilder: (context, index) { var item = _listItems[index]; return SizedBox( height: 300, child: Center(child: Text(item.toString()))); } ); } Don’t use OpacityWidget The Opacity widget can cause performance issues when used with animations because all child widgets of the Opacity widget will also be rebuilt in every new frame. It is better to use AnimatedOpacity in this case. If you want to fade in an image, use the FadeInImage widget. If you want to have a color with opacity, draw a color with opacity.\n⛔ Bad example\nOpacity(opacity: 0.5, child: Container(color: Colors.red)) ✅ Good example\nContainer(color: Color.fromRGBO(255, 0, 0, 0.5)) Precache images and icons Flutter contains a function precacheImage() which can be used to reduce the loading time of images and icons. You can use it in the initState() or didChangeDependencies() methods to speed up image display, for example.\nconst List\u0026lt;CatImage\u0026gt; images = [ CatImage(width: 150, height: 150, caption: \u0026#39;Watch out\u0026#39;), CatImage(width: 150, height: 160, caption: \u0026#39;Hmm\u0026#39;), CatImage(width: 160, height: 150, caption: \u0026#39;Whats up\u0026#39;), CatImage(width: 140, height: 150, caption: \u0026#39;Miaoo\u0026#39;), CatImage(width: 130, height: 150, caption: \u0026#39;Hey\u0026#39;), CatImage(width: 155, height: 150, caption: \u0026#39;Hello\u0026#39;), ]; @override void didChangeDependencies() { super.didChangeDependencies(); for (CatImage image in images) { precacheImage(NetworkImage(image.url), context); } } Reduce memory consumption of ListView A ListView has two properties (addRepaintBoundary and addAutomaticKeepAlives) that can lead to a high memory consumption in certain cases. They tell the ListView to not dispose of items which are not visible on the screen at the moment. In terms of performance, this is good because scrolling is fast. But the invisible items still occupy memory space. If you have a long list of images, this can lead to a problem. The solution is to set the mentioned properties to false (they are set to true by default).\nListView.builder( addAutomaticKeepAlives: false, addRepaintBoundaries: false, … ); Always use keys Every widget has a key constructor parameter and creates an element in the widget tree. A key allows us to reuse an element instead of creating a new one. You can either use ValueKeys or GlobalKeys. GlobalKeys can be tricky because there can never be multiple widgets with the same key in the widget tree. ValueKeys on the other hand will lead to bloaty code as you need to define them for each widget.\nConclusion In this article, you learned how you can boost your Flutter app performance with these 6 tips.\n","permalink":"https://quickcoder.org/posts/flutter-performance-tips/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eGet better scrolling and animations, less memory consumption, and more execution speed. Boost your Flutter app performance with these 6 tips. And if you want more, I have a free ebook with additional tips for you at the end!\u003c/p\u003e\n\u003cp\u003eHere are some easy-to-use best practices for Flutter apps when it’s all about performance. Use them if you notice any problems but don’t optimize prematurely. Flutter apps are fast by default and you won’t have any trouble usually. Here is how you can boost your Flutter app performance with these 6 tips.\u003c/p\u003e","title":"Boost Your Flutter App Performance With These 6 Tips"},{"content":"Introduction Learn how to choose the best data storage solution for your Flutter app between local storage, secure storage, or a cloud database. With this guide, they won\u0026rsquo;t be any open questions about what\u0026rsquo;s the correct approach for your app!\nDepending of what type of app you develop, you might need different data storage solutions. In this article, I’ll introduce various options for Flutter developers of how to manage data storage in your apps. Afterwards, you will be able to choose the best data storage solution for your Flutter app. The following topics will be covered:\nLocal storage Secure storage Cloud storage In this article, we will distinguish between two use cases for databases: storing large data blobs such as PDF files or images, and storing small key-value pairs. Whatever your needs may be, there is a solution available for you.\nLocal storage Local storage means that data is only stored on the user\u0026rsquo;s device. This is useful for apps that don\u0026rsquo;t require a permanent internet connection, or perform all operations on the device itself.\nLocalstore is a persistent document store for many small JSON files. To store large data chunks, it is better to write directly to files.. If you need a key-value store, there is Shared Preferences or Hive available for you. Every approach presented here will store the data persistently, so that it is available again when the user launches the app.\nLet’s dive into some code examples!\nLocalstore Localstore is a persistent NoSQL database based on JSON files. It is suitable for smaller JSON files, but not for larger ones. In such cases, it is better to read and write the files directly, without using a database.\nEasy and simple approach to handle multiple small JSON files in your app!\nReading and writing files The most basic approach for data storage in Flutter is to read and write files. In fact, every package that uses persistent storage does this in the background, although you may not notice it.\nReading and writing files in Flutter is not difficult. However, it does require more code than using a package. On the other hand, you have more control over the process. Simply import dart:io and you\u0026rsquo;re ready to go. Here are the steps:\nYes, it is more code and you most likely want to write a wrapper in your app, but you have the maximum amount of control with this approach.\nSharedPreferences Shared Preferences is a useful tool for persisting small amounts of data, such as user settings. It\u0026rsquo;s a key-value store similar to a Dictionary in Dart. The API is easy to learn, with getter and setter methods for various types like Int, Double, Bool, String, and StringList.\nI recommend storing only user or app settings in Shared Preferences. Create a class for all settings, implement a toJson() method, and use jsonEncode() to generate a string from it. Then save it with setString() and load it with getString() whenever you need it. If you are not familiar with JSON handling in Dart, have a look at this tutorial.\nHere is a code example showing how to read and write data with the Shared Preferences package:\nFor simple use cases, Shared Preferences might be a viable solution. However, serious app developers often prefer other approaches.\nHive Hive is a lightweight and blazing fast key-value database written in pure Dart. It works on all platforms, is simple to use, and has no native dependencies. Similar to Shared Preferences, you can use Hive as a Dictionary. However, a key-value store is called Box. The process is simple:\nOpen a box Read or write data Close the box (but that’s optional) The first step is always to initialize Hive. This needs to be done exactly once as long as the app runs. For Flutter apps, just add the code await Hive.initFlutter(); somewhere before you access a box for the first time.\nYou can store any primitive data in a box. Here\u0026rsquo;s an example:\nWith a type adapter, you can even store custom objects in your database. The class must derive from HiveObject and the properties must be annotated. Then, you can also benefit from save() and delete() methods!\nIf your boxes contain a lot of data and you want lazy loading, use a LazyBox. For higher security, Hive also provides EncryptedBoxes with AES 256 CBC and PKCS7 padding.\nHive has everything that a key-value database needs to offer. If you are still unsure whether Hive is the right choice for your next project, read these hints on when to use Hive and when not to use it.\nSecure storage To store data securely, you can use an encrypted version of Shared Preferences called Flutter Secure Storage. Alternatively, Hive also offers encrypted boxes. These work similarly to the default boxes in the previous example, but with the added benefit of secure storage at the cost of speed.\nFlutter Secure Storage Flutter Secure Storage is a useful tool for storing data securely. It is an encrypted version of Shared Preferences and allows you to store sensitive data such as user authentication tokens, API keys, and other confidential information. It provides a read and write method for any type of data, and it is easy to use with a simple and intuitive API.\nThis package uses the secure storage of the host system and is compatible with iOS, Android, MacOS, Linux, Windows, and the Web platform. Please refer to the documentation for instructions on how to configure the package for each platform.\nHere is a code example to get you started:\nRemember to add flutter_secure_storage to your pubspec.yaml file before using it.\nCheck out the Flutter Secure Storage documentation for more details and options.\nHive with encrypted boxes We previously discussed Hive. One major benefit is that it also provides encrypted boxes to securely store your data.\nAll you need is an encryption key that Hive can generate for you. You need to store that encryption key in a safe location. The following code example uses Flutter Secure Storage, but you can also use other packages or methods if you prefer.\nLet’s have a look at the code:\n🔔 Hint\nOnly values are encrypted, keys are stored in plain text If you use a wrong encryption key, you may get confusing results. There is no check if a key is correct! Storing the encryption key in a secure location can make this solution somewhat cumbersome.\nCloud storage When choosing the best data storage solution for your Flutter app, you should always consider cloud-based storage options. A lot of Flutter developers use Firebase as their cloud solution due to its easy integration with Flutter apps. Firebase provides a NoSQL database called Firestore to store JSON documents, as well as a Storage that acts as a file store.\nFirestore Firestore is a NoSQL document database and part of the Google Firebase services. It uses a hierarchy of collections and documents to structure data and is optimized for large collections of small documents. It is not useful, or even possible, to store entire files in it.\nThe Flutter package for Firestore uses well-known methods like get, update, set, and delete to manipulate data. Here is some example code on how to deal with it.\nTo get a quick start with Firestore, check out my detailed article below:\nStorage Storage is a blob storage service and part of the Google Firebase services. It is useful when you want to store PDF, images, or text files in a cloud environment. With its Flutter package, you can easily upload and download files as you like. Have a look at the following code examples.\nGet a storage reference (file pointer)\nRead data\nWrite data\nFor more details about Firebase Storage, read my detailed article below:\nConclusion In this article, you learned how to choose the best data storage solution for your Flutter app. In conclusion, there are various data storage solutions available for Flutter developers, depending on the type of app being developed. This article covered local storage, secure storage, and cloud storage options, as well as use cases for storing large data blobs and small key-value pairs. With the appropriate storage solution, developers can ensure that their app\u0026rsquo;s data is managed efficiently and securely.\n","permalink":"https://quickcoder.org/posts/choose-the-best-data-storage-solution-for-your-flutter-app/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eLearn how to choose the best data storage solution for your Flutter app between local storage, secure storage, or a cloud database. With this guide, they won\u0026rsquo;t be any open questions about what\u0026rsquo;s the correct approach for your app!\u003c/p\u003e\n\u003cp\u003eDepending of what type of app you develop, you might need different data storage solutions. In this article, I’ll introduce various options for Flutter developers of how to manage data storage in your apps. Afterwards, you will be able to choose the best data storage solution for your Flutter app. The following topics will be covered:\u003c/p\u003e","title":"Choose the Best Data Storage Solution for Your Flutter App"},{"content":"Introduction Let\u0026rsquo;s learn how to add a Google map to your Flutter app and use location services to display the current position. Everything you need to know to get started with Google Maps in Flutter is right here!\nIf your app relies on location data and a map, you can integrate a Google map quite easily. I’ll guide you through the setup process and show you how you can customize the map the way you want it to be. Here is how to add a Google map to your Flutter app.\nInstalling the package Run the command flutter pub add google_maps_flutter to install the package.\nGetting the API key To use the widget, you need to request an API key.\nGo to https://console.cloud.google.com/ and choose your project. If you don’t have an account or project set up, follow the instructions to do so Use the search bar and go to Google Maps Platform Head over to the APIs submenu Select Maps SDK for Android and activate it Google Maps for Android\nSelect Maps SDK for iOS and activate it Google Maps for iOS\nAfter enabling the SDKs go to Credentials and create new credentials with the button in the top bar Create Google Maps Credentials\nYour API key is ready to be used in our Flutter app!\n❗Warning\nIn a production app, you should read about access limitations for your API key. For this demo, it won’t be necessary. Details can be found here.\nConfiguring the app Depending on the platform you want to support, additional configuration steps are required.\n✅ (Android) Your minimum Android SDK version must be set to 20 or higher\nIn your Flutter project directory go to android/app/build.gradle and search for minSdkVersion inside the defaultConfig section. Replace the value (or Flutter variable) with 20.\n✅ (Android) Your API key must be added to the application manifest\nIn your Flutter project directory go to android/app/src/main/AndroidManifest.xml and find the \u0026lt;application... node. Add the following code inside the node and replace the value with your created API key.\n\u0026lt;meta-data android:name=\u0026#34;com.google.android.geo.API_KEY\u0026#34; android:value=\u0026#34;YOUR_API_KEY\u0026#34;/\u0026gt; ✅ (iOS) Your minimum iOS version must be 9.0 or higher\nThis should already be the case because Flutter itself requires that version. Otherwise, check this article at the step iOS Deployment Target.\n✅ (iOS) Your API key must be added to the application delegate\nIn your Flutter project directory go to ios/Runner/AppDelegate.swift and adjust the code as seen below\nimport UIKit import Flutter import GoogleMaps @UIApplicationMain@objcclassAppDelegate:FlutterAppDelegate { overridefuncapplication( _ application:UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey:Any]? ) -\u0026gt; Bool { GMSServices.provideAPIKey(\u0026#34;YOUR_API_KEY\u0026#34;) GeneratedPluginRegistrant.register(with:self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } } You have configured your app properly. Let’s move on to integrate a Google Maps widget.\n❗Warning\nNewer versions of the package might require different settings. Make sure to check the setup guidelines and adjust your code accordingly.\nAdding the widget To add a map, you can use the following code. It will display a map with the center over Berlin in Europe. For a more complex version, please refer to the GitHub repository with code samples.\nThere are a lot of properties to configure the map like rotateGesturesEnabled, zoomGesturesEnabled, trafficEnabled, or liteModeEnabled (only for Android). Customize it completely your way!\nHandling events The widget offers various events that you can handle\nonTap onLongPress onCameraMove onMapCreated The onMapCreated event returns a GoogleMapController which gives you complete control over the map. Some exposed methods are\ngetLatLng - returns the latitude/longitude combination of a screen position takeSnapshot - returns a byte array of the current map image moveCamera - jumps to a given position (see section “Moving the camera”) animateCamera - moves to a given position (see section “Moving the camera”) showMarkerInfoWindow - shows the info window of a given marker (see section “Adding markers”) Adding markers The following example will display a rotated blue marker pin in Berlin/Europe.\nBy default, a tap on the marker centers the map over it. If an InfoWindow is defined, it will be displayed. The onTap event will only be handled if consumeTapEvents is set to true. In that case, the InfoWindow is ignored. You can also use the GoogleMapController to trigger the InfoWindow.\nAdding circles The following example will display a circle around the center of Berlin/Europe.\nThe radius is denoted in meters. You can adjust the style with fillColor, strokeColor, and strokeWidth and handle the onTap event if required.\nSetting custom marker images The following code sets a custom marker image by using the asset image “icon.png” in Munich/Europe.\nMake sure to reference the asset image in your pubspec.yaml file.\nMoving the camera The GoogleMapController (see section “Handling events”) can be used to move the camera so that you can show a specific location to the user. It will either jump (moveCamera) or scroll (animateCamera) to the target position. An example looks like this:\n// jumps to Berlin/Europe await controller.moveCamera( CameraUpdate.newLatLng(LatLng(52.52309894124325, 13.413122125924026))); // scrolls to Berlin/Europe await controller.animateCamera( CameraUpdate.newLatLng(LatLng(52.52309894124325, 13.413122125924026))); Showing the current device location The google maps widget has the property myLocationEnabled. If set to true your current location will be marked as a blue dot on the map. To make this work, your app needs to ask for permission to use location services. If location services are enabled and permission was granted, the location will automatically be displayed.\n🔔 Hint\nYou don’t need to install another package for location, but you need to add the capabilities for Android and iOS. The Google Maps widget has built-in location support without any further requirements.\nSource code The source code is available on GitHub. Be aware that you need to replace the API keys inside the code with your own to make the demo code work!\nYou can see a screenshot of the app below.\nMaps widget of the demo app on GitHub\nConclusion In this article, we talked about adding a map to a Flutter application. Though the setup might be a bit time-consuming, it will be worth the trouble. The Google Maps widget is quite powerful and allows you to enrich your app in a very unique way.\n","permalink":"https://quickcoder.org/posts/flutter-google-map/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eLet\u0026rsquo;s learn how to add a Google map to your Flutter app and use location services to display the current position. Everything you need to know to get started with Google Maps in Flutter is right here!\u003c/p\u003e\n\u003cp\u003eIf your app relies on location data and a map, you can integrate a Google map quite easily. I’ll guide you through the setup process and show you how you can customize the map the way you want it to be. Here is how to add a Google map to your Flutter app.\u003c/p\u003e","title":"How To Add A Google Map To Your Flutter App"},{"content":"Introduction Here is a short guide on how to create Windows apps with Flutter. Learn how to build executables and MSIX packages. Use the power of Flutter Desktop and create beautiful apps for Windows!\nSince the release of Flutter 2.10, the Windows desktop is flagged as stable. In this article, I am going to show you what needs to be done to build executables and MSIX packages. So here is how to create Windows apps with Flutter. If you didn’t catch the news, here are the relevant sources about all the changes.\nWhat\u0026rsquo;s New in Flutter 2.10 Announcing Flutter for Windows Upgrading Flutter If you haven’t already upgraded your Flutter version, head over to the Flutter release page, download the newest version for your operating system, and install it.\nOr just use the command-line tool of your choice and execute flutter upgrade. This will do all the work for you. Of course, this is only possible if you already have installed an older version of the Flutter SDK on your system.\nIf you don’t know your current configuration, call the flutter doctor command and check the results.\nFlutter doctor output after upgrading to Flutter 2.10\nBuilding executables To create an executable file for your Flutter project you need to run the command flutter build windows. The created files including the application will be put in the subfolder build\\windows\\runner\\Release. The app is not self-contained, meaning it depends on the data folder and the DLL file. If you want to distribute the app, make sure to include the required data as well.\nOutput files after executing the build command\nBuilding MSIX packages MSIX packages provide install and uninstall routines and contain everything needed to run the app. Therefore, they are much better suited if you want to distribute your app. Building MSIX packages requires the MSIX package. You can add it to your project either by running the command flutter pub add --dev msix or by adding the reference manually to your pubspec.yaml file.\nMSIX dev dependency added to pubspec.yaml\nTo create the package create the executable first and then the package by using these commands\nflutter build windows flutter pub run msix:create The created package is stored in the same subfolder as the executable build\\windows\\runner\\Release.\nOutput files after executing the build and package command\nYou can distribute the MSIX package to any user running a recent Windows 10 or Windows 11 version and they can install it. It’s even possible to use the Windows Store for distribution. If you want to customize the package information, you just need to add a msix_config section to your pubspec.yaml or pass the information as arguments to the msix:create call. Check out the documentation of the MSIX package to find all available arguments. See the image below for a small example:\nConfiguration for MSIX packages in pubspec.yaml\nThe installer will look like this (the language depends on your system language):\nExample of the installation routine of a MSIX package\nUse Fluent Design Language If you want your app to look like a real Windows application, I recommend to use Microsoft’s Fluent Design Language. Thankfully, there is a Flutter package, that provides almost everything you need to style your app in a Windows fashion. The fluent_ui package contains nearly all controls, fonts, icons, etc that you can use within a Flutter app. The documentation is very good and there is even a mapping between Material controls and Fluent UI controls which comes in handy if you want to modify an existing app. The images below can give you a first impression of the differences.\nFlutter demo app with Fluent Design Language\nFlutter demo app with Material Design Language\nConclusion In this article, we saw how to build executables and MSIX packages with the Flutter 2.10 release. In addition, we took a look at how to style our app with the Microsoft Fluent Design Language.\nSource code You can find the source code on GitHub.\n","permalink":"https://quickcoder.org/posts/flutter-win-demo/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere is a short guide on how to create Windows apps with Flutter. Learn how to build executables and MSIX packages. Use the power of Flutter Desktop and create beautiful apps for Windows!\u003c/p\u003e\n\u003cp\u003eSince the release of Flutter 2.10, the Windows desktop is flagged as stable. In this article, I am going to show you what needs to be done to build executables and MSIX packages. So here is how to create Windows apps with Flutter. If you didn’t catch the news, here are the relevant sources about all the changes.\u003c/p\u003e","title":"How To Create Windows Apps With Flutter"},{"content":"Introduction Learn how to mock dependencies in your Flutter tests. Create, configure, and use mocks with the mockito package. Make your apps more robust and detect bugs earlier with proper testing.\nDuring testing, there will always be a point where you need a mock. This mock will save you from setting up complex environments for a test because it allows you to focus on the parts you want to test. Assume you have a logger in multiple places in your application that writes into a database. Of course, you have tests for your logger to ensure its correct functionality. But when you test, for example, your login mechanism, you don’t care about the logger. That’s where a mock is useful. It handles the calls to the logger and reacts the way it was configured. Here is how to mock dependencies in your Flutter tests.\nIn this guide, we are looking at the Mockito package for Flutter apps. It allows you to create mocks so that any dependencies of your classes don’t need to be handled for testing.\nTesting a class Here is the DataService class, we are going to write tests for. It takes a DataStore object as a constructor argument. The DataStore could be anything from a remote database over the local storage on the device to a simple Map\u0026lt;string, dynamic\u0026gt;() object that stores the values. We don’t want to deal with this dependency, so we are going to mock it in our test.\nimport \u0026#39;data_store.dart\u0026#39;; class DataService { final DataStore store; DataService(this.store); dynamic get(String identifier) { return store.get(identifier); } void add(String identifier, dynamic data) { store.add(identifier, data); } void update(String identifier, dynamic data) { store.update(identifier, data); } void remove(String identifier) { store.remove(identifier); } } Generating mocks The build runner tool of Dart helps us to create mocks for every class we want. We add it to our Flutter app with the command flutter pub add build_runner. Afterward, we specify the classes for which a mock should be generated. We add an annotation at the main() method of our empty test class (see code below).\n@GenerateMocks([DataStore]) void main() {} And finally, we call the build runner with flutter pub run build_runner build. It will create a new class that contains all mocks specified in the array of the annotation.\nBuild runner output\nClass with mocks\nUsing mocks To test the get() method, we need to set up the mock. Otherwise, an exception will be thrown, because since Dart 2.12/Flutter 2 and Null Safety there isn’t a common default value like null that could be returned.\nTo specify the behaviour, we use the when() and thenAnswer() methods. You can see it in the image below.\ntest(\u0026#34;Verify get returns values\u0026#34;, () { var storeMock = MockDataStore(); var service = DataService(storeMock); when(storeMock.get(any)).thenAnswer((_) =\u0026gt; \u0026#34;I am a value!\u0026#34;); expect(\u0026#34;I am a value!\u0026#34;, service.get(\u0026#34;dummy\u0026#34;)); verify(storeMock.get(any)).called(1); }); When the get() method of the mock is called, it always answers with a fixed value that we can verify afterward. Additionally, we can ask the mock if the get() method was called to be more confident.\n⛔ Error\nThe following hint is not the recommended behavior and should only be used for legacy apps.\nIf you want your mocks to return null when there is no setup available, then use replace your annotation\n@GenerateMocks([DataStore]) with\n@GenerateMocks([],customMocks: [MockSpec\u0026lt;DataStore\u0026gt;(returnNullOnMissingStub: true)]) Then your test could look like this\ntest(\u0026#34;Verify get returns values\u0026#34;, () { var storeMock = MockDataStore(); var service = DataService(storeMock); service.get(\u0026#34;dummy\u0026#34;); verify(storeMock.get(any)).called(1); }); Source code You can find the source code on GitHub.\n","permalink":"https://quickcoder.org/posts/flutter-dependency-mocking/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eLearn how to mock dependencies in your Flutter tests. Create, configure, and use mocks with the mockito package. Make your apps more robust and detect bugs earlier with proper testing.\u003c/p\u003e\n\u003cp\u003eDuring testing, there will always be a point where you need a mock. This mock will save you from setting up complex environments for a test because it allows you to focus on the parts you want to test. Assume you have a logger in multiple places in your application that writes into a database. Of course, you have tests for your logger to ensure its correct functionality. But when you test, for example, your login mechanism, you don’t care about the logger. That’s where a mock is useful. It handles the calls to the logger and reacts the way it was configured. Here is how to mock dependencies in your Flutter tests.\u003c/p\u003e","title":"How To Mock Dependencies In Your Flutter Tests With Mockito"},{"content":"Introduction A large app contains many resources in different places. Learn how to organize your Flutter App assets, styles, colors, images. Here are some ideas to give more structure to your projects.\nIn this article, we are looking at resource management in Flutter apps. A resource in this context is, for example, a style, an image, a constant, a utility, or a color that will be used within your app. I will show you ways how to organize them inside your app so that it will still be manageable despite a large codebase. The advantage is that you don’t have redundant code parts and if you want to change anything you will only have to look at one place in your source code. Let’s get started right away with some ideas on how to organize your Flutter app assets, styles, colors, images!\nThe examples will mostly use colors, but this is just to demonstrate the ideas. You can use it for anything like constants, utilities, resources, and so on.\nIdea 1: Global declarations The most basic approach. Just put everything into a single file as a global definition and access it from anywhere. This will get messy very soon, so consider splitting it up into several files. Also, the descriptions might get long to distinguish between a background color for a scaffold and a background color for a button for example. Good tooling is essential in this case (proper code navigation with editors like VS Code)!\nIdea 2: Single class per resource type This idea is still pretty simple. We have one class containing all colors, one containing all text styles, and so on. So if you want to access a color in code, you just do AppColors.background. The naming convention is always: type of resource dot description.\nWhat type of resource do I need? I need a color → AppColors class\nWhat should the resource be used for? It will be the background of a scaffold → background property\nThis is straightforward and easy to understand but will lead to many color definitions and similar descriptions in a single file. You’ll also need to import one file per resource which could lead to a longer import list. I’d choose this approach only for smaller apps.\nThe example makes use of private classes and static references to create the desired relations.\nIdea 3: Single class per resource type with grouping The next idea introduces another hierarchy level but works like the previous idea in general. However, you will need to call AppColors.scaffold.background to get the color from the previous example. This approach adds a widget level to the hierarchy so that you can cluster your colors per widget. If you don’t like the widget hierarchy idea, try it with a view, use case, or state hierarchy instead. You still have multiple files to import to access all resources but the organization is more structured than before.\nIdea 4: Grouped resource hierarchy structure Finally, we can also combine all of our resources into one single structure to simplify access even more. This reduces necessary import statements and defines a consistent access logic that can be used anywhere in the app. An example call will now look like this: Resources.colors.scaffold.background.\nTip: How to reduce import statements You can use the export keyword to combine multiple imports into a single one. Be aware that this might be confusing to some developers if used incorrectly because it is more difficult to see where the actual code is located. Here is an example of how to do it:\nWorking with assets and fonts An asset (or resource) is a file that is included in the app binary and loaded at runtime. Popular examples are images and JSON files. Assets and also fonts are stored in a custom project folder and are referenced in pubspec.yaml, so that the app knows where to load the data from. The pubspec.yaml contains examples of how to include them. For further details, check the official documentation for assets and fonts.\nIn this section, I want to show approaches to prevent hard-coded strings in multiple locations in your app code. A simple typo can create an exception at runtime that you might miss. So, here are two approaches to prevent such behavior.\nCreate a class for your assets and fonts In the previous sections, I provided some example ideas that you can use here, too. Anything is better than using strings throughout the app. If you need to change anything you have exactly one place where to do that. A little example:\nclass Assets { static String splashImage = \u0026#34;assets/splash.png\u0026#34;; static String saveIcon = \u0026#34;assets/save.png\u0026#34;; } To ensure that your files exist, you can write a unit test. Here is a small example:\nimport \u0026#39;dart:io\u0026#39;; void main(){ test(\u0026#34;Verify all used images exist\u0026#34;, () async { expect(await File(\u0026#34;assets/splash.jpg\u0026#34;).exists(), isTrue, reason: \u0026#34;no image for splash\u0026#34;); expect(await File(\u0026#34;assets/save.jpg\u0026#34;).exists(), isTrue, reason: \u0026#34;no image for save\u0026#34;); }); } If that is too much of a hassle, check the next tip.\nUse the flutter_gen package to create the wrapper classes for you The flutter_gen package contains a build runner that scans your pubspec.yaml for assets and automatically creates wrapper classes. You can use these wrappers to access the assets in your widgets. The big advantage is that the package will skip files that aren’t found on the hard disk. So, if you have a typo in your asset definition, there will be no class for that asset. You’ll immediately notice it when you want to reference it in the code.\nSource code You can find the source code on GitHub.\nConclusion With these tips, it should be easy for you to organize your app. Choose one of these ideas or combine them to suit your specific needs. It always depends on multiple factors what the right approach is. There is no general rule for every use case.\n","permalink":"https://quickcoder.org/posts/flutter-assets/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eA large app contains many resources in different places. Learn how to organize your Flutter App assets, styles, colors, images. Here are some ideas to give more structure to your projects.\u003c/p\u003e\n\u003cp\u003eIn this article, we are looking at resource management in Flutter apps. A resource in this context is, for example, a style, an image, a constant, a utility, or a color that will be used within your app. I will show you ways how to organize them inside your app so that it will still be manageable despite a large codebase. The advantage is that you don’t have redundant code parts and if you want to change anything you will only have to look at one place in your source code. Let’s get started right away with some ideas on how to organize your Flutter app assets, styles, colors, images!\u003c/p\u003e","title":"How To Organize Your Flutter App Assets, Styles, Colors, Images"},{"content":"Introduction Using Firebase Cloud Messaging and Firebase Cloud Functions we can achieve this. Learn how to send push notifications with Firebase. This vital feature needs to be part of every app in today\u0026rsquo;s world!\nToday we are going to focus on how to send push notifications from a Flutter app. Therefore Firebase will be used as it provides packages that can be used within a Flutter app. In this scenario, a push notification will be triggered and sent to your own device. Firebase offers some of its services only for paying users but they charge you for your usage. If you don’t use the services, you won’t be charged anything. So here is how to send push notifications from Flutter apps with Firebase to any device.\nOur final workflow will look like this:\nFrom our app, a message with custom text is sent to Firebase Cloud Functions. The function will forward our message to the Firebase Cloud Messaging (FCM) service. The service will deliver the message to the target device. Our app should receive a push notification within some seconds. Workflow of the message\n⛔ Error\nUnfortunately, at the moment there is no possibility for us, to send messages from app to device without using Firebase Cloud Functions as the FCM Flutter package does not provide a method for it. So we have to use the workflow above.\nThe next steps of this article are as follows:\nCreate and set up Firebase account and project Prepare Flutter app Write code to send and receive push notifications Firebase Setup Before the fun part starts we need to set up the backend so that our app can send and receive push notifications. We will create a Firebase project and a Firebase app for each supported platform (iOS, Android, Web). If your Firebase environment is already configured, you can skip this part.\nCreate a new Firebase project If you don’t already have one here is a short guide. Otherwise, you can skip this part.\nGo to console.firebase.google.com and log in with your Google account. Click Add project Choose a name Google Analytics can be skipped Create the project Firebase page after successful project creation\nChange plan for Firebase Cloud Functions To use Cloud Functions you need to upgrade your project from the Spark plan (free usage) to the Blaze plan (pay per usage). This requires payment information to be entered (for example a credit card or you can also use Google Pay).\n🔔 Hint\nThis example won’t cause any recurring costs, but if you extend your Firebase usage above the free limits, you will be charged for it. Keep that in mind! Pricing details can be found here.\nTo change the plan, go to the Cloud Functions area, hit the Upgrade project button, and follow the instructions (see images below)\nLink to the Cloud Functions area\nUpgrade notification when using Cloud Functions for the first time\nAfter the upgrade, you should see an empty dashboard. Your deployed functions will be available here, where you can check logs, health status, and usage statistics.\n❗Warning\nPlease be aware that a deployed cloud function occupies resources which can lead to costs. If you deploy more functions, the costs might increase. You can use a budget alert to get notified when unexpected costs arise.\nApp Setup To send push notifications with Firebase, we need to prepare our Flutter app. The first step is to link our app with the Firebase backend. After that, we will focus on writing code to send and receive push notifications.\nLink with Firebase backend This is a pretty easy task nowadays as there is a command-line tool that will support us with the configuration. First, we need to add the Firebase Core dependency to our project. Run the command\nflutter pub add firebase_core from your app’s root folder. The next command installs a cli to connect our app with Firebase. Run\ndart pub global activate flutterfire_cli and wait until the installation process has finished.\nFinally, run the command\nflutterfire configure It will guide you through the setup process, create Firebase apps and link them with your Flutter app. Afterward, there will be a Dart file containing all relevant Firebase account information. Do not check this file into version control ❗.\nInstalling the Firebase packages In your Flutter project open the pubspec.yaml file, add the Firebase Messaging package, and the Cloud Functions package (see image below).\nflutter pub add firebase_messaging flutter pub add cloud_functions Firebase Messaging Cloud Functions Required Firebase packages in pubspec.yaml\n🔔 Hint\nThe packages are updated frequently, so the versions in this example might be outdated.\nEnable app capabilities for iOS If you are targeting iOS as a platform, you need to do some more configuration work to enable push notifications. One particular requirement is that you have an active Apple developer account. It won’t work without it. The following article guides you through the complete setup process:\nHow to configure your XCode project How to register keys, provisioning profiles, and identifiers in the Apple Developer portal FCM via APNs Integration Android apps don’t require additional setup steps.\nSet up cloud functions A handy helper for everything related to Firebase are the Firebase CLI tools (installation guidelines). We will use them to initialize the cloud functions and to update them. After the installation, run the command firebase init functions. It will guide you through the login and setup process. Be sure to select the Firebase project you created earlier. Finally, you will see a functions folder in your project directory. It contains the deployed functions and their dependencies. The setup is completed ?.\nfunctions folder for Firebase Cloud Functions\nCode Setup Finally, we are getting to the most interesting part. Here we will create the cloud function that delivers our message to Firebase Cloud Messaging. And we will implement handlers for incoming notifications so that our app displays the notifications.\nSetting up the cloud function Here is the code for the Cloud Function, we will use. It takes a data argument supplied by the caller and calls the Cloud Messaging service. The result indicates the success or failure of the operation.\nThe data object is a Map that should have 3 properties:\ntargetDevices is a list of strings containing the tokens of the target devices\nmessageTitle is a string containing the title of the message\nmessageBody is a string containing the body of the message\nInsert this code into the index.js file in your functions folder. Then run the command firebase deploy --only functions. Your dashboard in Firebase should be updated after the operation is finished. The function is ready to be called ?\nFirebase Functions dashboard after deploying a function\nOutput of the deployment process\nRegister for incoming notifications When the app is not running all the notifications are handled by the operating system. You get your typical sound and the app icon in the notification bar just as you are used to. To handle incoming notifications while our app is running we need to register a handler function. If there is no handler the message is just swallowed and you don’t even know that you received one.\nAfter our handler is registered and a notification is received while the app is running, the title and body will be printed to the console.\nA background handler has the restriction that it must be a top-level function. It will be invoked when a notification is received and the app is in the background.\nSend a push notification To send a push notification we need to get the device token of the target device (in this case our own device). We then send the message to the Firebase Cloud Functions service which delivers it to the Firebase Cloud Messaging service and it handles everything else. If for example, the target device is offline the message is transmitted as soon as the device is online again. All the little details we don’t need to worry about.\nDevice token is printed to the console on app launch\nThe code tries to find a Firebase function with the name notifySubscribers. When found, it invokes the function with an argument that contains the target devices, the message title, and the text, the user entered in the UI. A print message in the console tells us if the call was successful.\nCustomize a push notification You can add additional data to a push notification and have your app handle it in different ways. The RemoteMessage object has a data property where you can add your custom data to the request. Your app can read these values and react accordingly. The image below shows a modified version of our cloud function using the data property.\nIf you want to add platform-specific code like icons or custom click actions, read this section of the documentation for details.\nSend a test notification from Firebase Firebase allows sending customized push notifications from its UI. That way you can test for example how a notification looks when your app is not running. Follow these steps to send a notification.\nOn the Firebase page go to Cloud Messaging. Menu overview on the Firebase page\nClick on Send your first message. Send your first message with Cloud Messaging\nEnter at least a title and a description and click Send test message. Send a test message with Cloud Messaging\nAdd your device token and click on Test. Eventually sending the message to the target device\nYour device should receive a push notification. The handling depends on whether the app is in the foreground, in the background, or not running at all.\nWhen the app is not running at all, you’ll get a hint in your notification tray of the mobile OS. Received push notification from Firebase when app is in background\nReceived push notification from Firebase when app is in background\nWhen the app is running in the background, you’ll get a hint in your notification tray of the mobile OS and the background handler function will kick it if it is set (see possible output below). Output when receiving a notification while app is in background\nWhen the app is running in the foreground, the foreground handler function will handle the message if set (see possible output below). Output when receiving a notification while app is in foreground\nWhen your app is running (foreground or background) and a notification is received you can use the package flutter_local_notifications to display a real notification. That way the user is notified properly. Another approach is to display a banner in your app (with a Snackbar widget, for example) but it’s up to you how to continue from here.\nConclusion In this article, you learned how to send push notifications with Firebase. This is the shortest way possible to enable a Flutter app to receive AND send push notifications to other devices. The configuration of the backend might be a bit overwhelming at first, but it’s really not that hard. With this knowledge, you can interact more easily with your app users in an anonymous way.\nSource code You can find the source code on GitHub. The repo contains the working app without the Firebase configuration. You need to create the account, link your app with it and deploy the cloud function. Then you can reproduce all the results of this article.\n","permalink":"https://quickcoder.org/posts/flutter-push-notifications/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eUsing Firebase Cloud Messaging and Firebase Cloud Functions we can achieve this. Learn how to send push notifications with Firebase. This vital feature needs to be part of every app in today\u0026rsquo;s world!\u003c/p\u003e\n\u003cp\u003eToday we are going to focus on how to send push notifications from a Flutter app. Therefore \u003cstrong\u003eFirebase\u003c/strong\u003e will be used as it provides packages that can be used within a Flutter app. In this scenario, a push notification will be triggered and sent to your own device. Firebase offers some of its services only for paying users but they charge you for your usage. If you don’t use the services, you won’t be charged anything. So here is how to send push notifications from Flutter apps with Firebase to any device.\u003c/p\u003e","title":"How To Send Push Notifications With Firebase"},{"content":"Introduction Let’s have a look at how to use Flutter image picker and Firebase storage to save your images in the cloud. Create device-independent apps so that your users can always access their data!\nSaving images in the cloud is a convenient way to ensure that it is available on any device. In this article, I want to show you how to use Flutter image picker and Firebase storage to do exactly that. We will pick a file (or take a picture), upload it to Firebase Storage, and display it in our app. Let’s get started!\nImage picker Our app needs some packages to make it as easy as possible for us. The first one is image_picker. It offers a dialog to select a file from your gallery or take a picture if your device has a camera. This works on all major platforms. Here is how you use it:\nfinal image = await ImagePicker().pickImage(source: ImageSource.gallery); if (image != null) { final imageFile = await image.readAsBytes(); ... } You can also use ImageSource.camera to trigger the camera dialog. After you have taken a picture, it will be returned by the pickImage() method. Usually, you would trigger this when the user clicks a button or something like that.\nWe have our image. Let’s upload it to Firebase and display it in our app!\nFirebase We need the packages firebase_core and firebase_storage. I assume you are familiar with creating a Firebase project and configuring Firebase Storage. If not, check out this introduction article.\nHere is how you can upload your file to Firebase Storage:\nfinal image = await ImagePicker().pickImage(source: ImageSource.gallery); if (image != null) { final imageFile = await image.readAsBytes(); final ref = FirebaseStorage.instance.ref(); final child = ref.child(\u0026#34;images/${image.name}\u0026#34;); final uploadTask = child.putData(imageFile); ... } This will upload the selected image to a subfolder images in Firebase Storage.\nThe last thing is to display the image.\nDisplay image Throughout our journey on how to use Flutter image picker and Firebase Storage, we have successfully selected an image using a dialog and uploaded it to Firebase. Now, we aim to display this image in a Flutter app.\nWe use the uploadTask from before and wait until it tells us that the upload has completed successfully.\nfinal image = await ImagePicker().pickImage(source: ImageSource.gallery); if (image != null) { final imageFile = await image.readAsBytes(); final ref = FirebaseStorage.instance.ref(); final child = ref.child(\u0026#34;images/${image.name}\u0026#34;); final uploadTask = child.putData(imageFile); uploadTask.snapshotEvents.listen((event) async { if (event.state == TaskState.success) { _imagePath = await event.ref.getDownloadURL(); setState(() {}); } else if (event.state == TaskState.error) { // handle error case } }); } When completed, we retrieve the download url and store it in a field.\nIn our build method, we have an Image that waits for this download url. While the url is null or empty, we just display nothing. It could look like this:\nString _imagePath = \u0026#34;\u0026#34;; @override Widget build(BuildContext context) { return Scaffold( body: Center(child: _imagePath.isNotEmpty ? Image.network(_imagePath) : const SizedBox())); } And this is it! We selected an image with a file picker, uploaded it to Firebase Storage, and displayed it in our Flutter app.\nWith this basic example code, you can enhance your app and make it cloud-ready.\nConclusion In this article, you learned how to use Flutter image picker and Firebase Storage to save your images in the cloud. With the provided code examples, you should be able to get started quickly and enhance your app the way you need it to be.\n","permalink":"https://quickcoder.org/posts/how-to-use-flutter-image-picker-and-firebase-storage/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eLet’s have a look at how to use Flutter image picker and Firebase storage to save your images in the cloud. Create device-independent apps so that your users can always access their data!\u003c/p\u003e\n\u003cp\u003eSaving images in the cloud is a convenient way to ensure that it is available on any device. In this article, I want to show you how to use Flutter image picker and Firebase storage to do exactly that. We will pick a file (or take a picture), upload it to Firebase Storage, and display it in our app. Let’s get started!\u003c/p\u003e","title":"How To Use Flutter Image Picker And Firebase Storage"},{"content":"Introduction Reduce database load and add offline capabilities to your apps. Here is how to enable Firestore cache in Flutter web apps. If you target Android or iOS, caching is enabled by default!\nIn case you didn’t know yet: Firebase Firestore can be used offline by enabling an internal cache. Your read and write operations work normally in offline mode and will synced with the database once the device is online again. This feature is enabled on Android and iOS by default. But for web apps you need to enable it yourself. Here is how to enable Firestore cache in Flutter web apps.\nEnable persistence Web apps don’t have persistence enabled by default because the browser might not support it. Currently, you are fine with Chrome, Safari, Firefox, and Edge. To enable persistance, use the following code:\nawait FirebaseFirestore.instance .enablePersistence(const PersistenceSettings(synchronizeTabs: true)); This must be the first call to Firestore after your app launches. Otherwise, it will fail with the following error message:\nFirebase error when calling enablePersistence: Firestore has already been started and persistence can no longer be enabled. You can only enable persistence before calling any other methods on a Firestore object.\nWith the synchronizeTabs parameter you can specify how your app should handle multiple tabs. If this value is false and a user opens your app in more than one tab, this call will fail after the first time. You could then show a message to only use your app in one tab at a time for example.\nTo set the cache size, use the following code:\nFirebaseFirestore.instance.settings = const Settings(cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED); However, you should be fine with the default value in general. When the cache is full, the oldest unused documents get cleaned up first.\nFix hot reload/hot restart error Flutter developers love hot reload and hot restart to quickly see the changes they made to their apps. But when you enable persistence in a Flutter web app, you’ll be greeted with the already mentioned error message:\nFirestore has already been started and persistence can no longer be enabled. You can only enable persistence before calling any other methods on a Firestore object.\nHot reload/hot restart don’t reset Firestore. That’s why you get the error. Closing and restarting the entire app works but it consumes a lot of time. On my system, a hot reload is about 500ms, a hot restart about 2-3 seconds, and full restart takes up to 30 seconds.\nSince there is no built-in solution in Firebase, I came up with this one:\ntry { await FirebaseFirestore.instance .enablePersistence(const PersistenceSettings(synchronizeTabs: true)); } catch (_) { } I simply wrap it in a try catch block and swallow the exception. This approach also works for hot reloads/hot restarts and I don’t need to wait for my app to completly shut down and boot up again. I don’t care if it’s perfect or not because it’s only relevant when debugging apps and I needed a way to make hot reload/hot restart work again.\nConclusion In this article, I showed you how to enable Firestore cache in Flutter web apps. It’s a mighty feature that can save you money and add offline capabilities with just one line of code.\n","permalink":"https://quickcoder.org/posts/how-to-enable-firestore-cache-in-flutter-web-apps/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eReduce database load and add offline capabilities to your apps. Here is how to enable Firestore cache in Flutter web apps. If you target Android or iOS, caching is enabled by default!\u003c/p\u003e\n\u003cp\u003eIn case you didn’t know yet: \u003ca href=\"https://firebase.google.com/docs/firestore/manage-data/enable-offline\"\u003eFirebase Firestore can be used offline\u003c/a\u003e by enabling an internal cache. Your read and write operations work normally in offline mode and will synced with the database once the device is online again. This feature is enabled on Android and iOS by default. But for web apps you need to enable it yourself. Here is how to enable Firestore cache in Flutter web apps.\u003c/p\u003e","title":"How To Enable Firestore Cache In Flutter Web Apps"},{"content":"Introduction Microsoft and Flutter aren’t the best friends. But anyway, here is how to deploy your Flutter web app to Microsoft Azure. If you are familiar with Firebase Hosting, then you\u0026rsquo;ll notice a few similarities!\nUsually, Firebase is my preferred choice when it comes to cloud services for Flutter apps. But recently, I played around with Microsoft Azure and to my surprise it works rather well. I have a Flutter web app that I wanted to deploy similar to Firebase Hosting. For Azure, Microsoft offers the service Static Web Apps as part of the Azure App Service. It supports a bunch of frameworks including Flutter. Here is how to deploy your Flutter web app to Microsoft Azure.\nWe are going to set up the system with pull requests. This means that every pull request creates a new preview environment. Testers can verify the changes and once the pull request is merged, the code changes are deployed to the production environment.\nIf you just want to publish a static website without the pull request workflow, you can do this with the Azure CLI. Have a look at this guide here for details.\nPrepare Azure The first step is to create a static web app. Use the Create a resource button in your Azure Portal and look for static web apps.\nCreate a static web app in Azure Portal to deploy Flutter web apps\nEnter the basic details like Subscription, Resource Group, Name, and Region. You also get a free plan for one app per subscription that you can use.\nFill out required fields when creating a static web app to deploy Flutter web apps on Microsoft Azure\nThe next step is to connect your version control system. In my case, I use GitHub. After authenticating, I can select one of my projects and a branch. This branch represents the productive environment. When a pull request targets this branch, the production environment is updated after the merge.\nConnect to your code repository to deploy Flutter web apps on Microsoft Azure\nThe configuration is complete but we need to edit the workflow file in the next step.\nEdit Workflow When you run the workflow initially, it will fail. The problem is that it is not configured properly. Here is the final workflow that I came up with:\nWhen you build a Flutter web app, the output folder targets build/web. We pass this path to the Azure/static-web-apps-deploy@v1 action for the properties app_location and output_location. In addition, we need to build the app before deploying it. That’s why I added subosito/flutter-action@v2. It installs the Flutter tooling in the pipeline so that we can use Flutter CLI commands.\n💡 Tip\nIf you are using Azure DevOps, the code changes are the same, but you might need to install a Flutter task runner like Flutter Tasks in your organization.\nThe workflow has two cases:\nWhen a pull request is created that targets the master branch, it will create a comment in the pull request with a link to the deploy preview version\nWhen a pull request is merged (or when you push to master directly), it will deploy to the productive environment and delete the corresponding preview version\nUpload the adapted workflow to your GitHub repository. Then, you can test if everything works as expected.\nDeployment Test As mentioned before, the workflow requires pull requests to work. Here are the quick steps for a test:\nCreate a new branch\nMake code changes to your website in that branch\nPublish the branch\nCreate a pull request\nAfter those steps, you should see the GitHub actions runner in your pull request. When it finishes, a comment with an URL to the deployed preview version should appear.\nGitHub actions runner result and comment with URL to the deployed preview version in GitHub.\nNavigate to the deployed version and verify that it contains the latest changes. When everything looks good, you can merge the pull request.\n💡 Tip\nYou can also trigger the deployment with a push to the master branch but in a professional environment, this is considered bad practice. Pull requests and feature branches should be used to make sure you don’t commit by accident.\nIn Azure, you can see all your different environments. Every preview environment links to an open pull request in your code repository. You can quickly navigate to the deployed websites or jump to the pull requests with the provided links in the view.\nHere is the static web app environment view in Microsoft Azure. The production environment targets the master branch while all other branches are deployed to preview environments.\nConclusion In this article, we talked about how to deploy your Flutter web app to Microsoft Azure. If you are familiar with deploying to Firebase Hosting, you’ll notice the differences and similarities. There are some pitfalls but it is possible with rather little effort.\n","permalink":"https://quickcoder.org/posts/how-to-deploy-your-flutter-web-app-to-microsoft-azure/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eMicrosoft and Flutter aren’t the best friends. But anyway, here is how to deploy your Flutter web app to Microsoft Azure. If you are familiar with Firebase Hosting, then you\u0026rsquo;ll notice a few similarities!\u003c/p\u003e\n\u003cp\u003eUsually, Firebase is my preferred choice when it comes to cloud services for Flutter apps. But recently, I played around with Microsoft Azure and to my surprise it works rather well. I have a Flutter web app that I wanted to deploy similar to Firebase Hosting. For Azure, Microsoft offers the service \u003ca href=\"https://azure.microsoft.com/en-us/products/app-service/static/\"\u003eStatic Web Apps\u003c/a\u003e as part of the \u003ca href=\"https://azure.microsoft.com/en-us/products/app-service/\"\u003eAzure App Service\u003c/a\u003e. It supports a bunch of \u003ca href=\"https://learn.microsoft.com/en-us/azure/static-web-apps/front-end-frameworks#configuration\"\u003eframeworks\u003c/a\u003e including Flutter. Here is how to deploy your Flutter web app to Microsoft Azure.\u003c/p\u003e","title":"How To Deploy Your Flutter Web App To Microsoft Azure"},{"content":"Introduction Brevo offers a powerful free bulk-email service. Here is how you can send emails for free with the Brevo REST API. Grab your free package with 300 emails per day with Brevo!\nIn this guide, I’ll show you how to send free emails with the Brevo REST API. While Brevo is more than just an bulk-email service, we will focus on that part here.\nWhat you need To use the REST API you need a free Brevo account and an API key. In the free version you can send up to 300 emails per day. When you exceed the limit, the API will deny your requests until the quota is reset.\nFirst, go to https://brevo.com and create a free account. This is enough to get you started. When you need more, you can always upgrade within minutes.\nNext, log into your account and head to the SMTP \u0026amp; API settings menu in the top right corner.\nNavigation menu of the Brevo website, The entry SMTP \u0026amp; API is highlighted.\nGo to the next page and click on the API Keys tab. This will show you all of your existing API keys.\nList of all existing API keys. The button Generate a new API key on the right side allows generating a new key.\nOn the right side, you can generate a new API key by clicking the Generate a new API key button. Give the key a name and store it in a safe location. Once you close the dialog window, the key cannot be restored!\nYou need to include the API key you created in your requests to the REST API. In the next step, we will assemble a request and test the sending process.\nBuild a request It doesn’t really matter what programming language you use. The essential part is building and sending an HTTP request with the correct JSON content so that the API knows what to do.\nTo get started, we will just send the text Hello world! as email content. You can customize it later once it works as expected.\nHere is the JSON that the REST API understands:\nWe need to provide the following properties:\nA sender object containing an optional name and an email address. An array of recipients as the to object. Each recipient should also include an optional name and an email address. A subject string. A valid HTML document string for the htmlContent property. This is a minimal example. The API offers many more options, such as attachments, scheduled sending, and BCC recipients. For all the details and examples available, please refer to the documentation.\nHere is the Dart code to build the request body:\nHere is the C# code to build the request body:\nFor C#, there is an official SDK available that allows you to send emails and perform other tasks. You can download it from GitHub or add it directly in Visual Studio using Nuget. This SDK will make your code easier to read and more robust.\n💡 Tip\nDon\u0026rsquo;t get confused because the package is labeled as Sendinblue. Brevo recently underwent a rebranding and was formerly known as Sendinblue. It appears that not everything has been updated yet.\nFor more information, please refer to the API documentation.\nSend a request We have built the content of the request. Now we need to send it. To make it work, the request needs the following header properties:\nAccept header with the value application/json Content-Type header with the value application/json Api-Key header with your individual API key The expected HTTP status code is 201 because we send a POST request and create a resource on the server.\nHere is the Dart code to send a request:\nTo make the code work you also need the http package which handles the request sending in this example.\nAnd here is the same example with C# code:\nYou will need the package RestSharp to make the code work.\nAfter sending the request you can track the sending progress in the Brevo dashboard.\nBrevo Real time dashboard with the verification that two emails were sent in the last 30 minutes.\nAnd that is all it takes to send emails for free.\nConclusion This article demonstrated how to send emails for free using the Brevo REST API. The code examples provided make it easy to integrate this functionality into your applications.\n","permalink":"https://quickcoder.org/posts/free-emails-brevo-rest-api/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eBrevo offers a powerful free bulk-email service. Here is how you can send emails for free with the Brevo REST API. Grab your free package with 300 emails per day with Brevo!\u003c/p\u003e\n\u003cp\u003eIn this guide, I’ll show you how to send free emails with the Brevo REST API. While Brevo is more than just an bulk-email service, we will focus on that part here.\u003c/p\u003e\n\u003ch1 id=\"what-you-need\"\u003eWhat you need\u003c/h1\u003e\n\u003cp\u003eTo use the REST API you need a free Brevo account and an API key. In the free version you can send up to 300 emails per day. When you exceed the limit, the API will deny your requests until the quota is reset.\u003c/p\u003e","title":"How To Send Emails For Free With The Brevo REST API"},{"content":"Introduction Firebase allows installing various extensions to further increase functionality. Here is how to add new Firebase features with extensions. Instead of writing everything from scratch, you can pay some cents and use a plug and play extension from Firebase!\nFirebase Extensions is an easy way to enhance Firebase and your app with additional capabilities without having to implement them yourself. They help with sending emails, translating text, resizing images, making payments, and other stuff. The possibilities are huge and it takes only minutes to set up these extensions. Instead of investing hours of development work to integrate 3rd parties into your app, look for an extension and save a lot of time!\nI’ll guide you through the setup process and present some of the extensions to you. It will give you an impression of what is possible.\n🔔 Hint\nBe aware that you might run into some costs. An installed extension costs around 1 cent per month and it normally uses other services in the background which can increase these costs. You will also need to activate the Blaze plan.\nFinding extensions There is an entire marketplace of Firebase extensions at https://extensions.dev/extensions. There are filter options for Firebase products, categories, or providers available. You can also do a text search. But I recommend browsing the catalog to get inspired by the possibilities.\nInstalling extensions Choose an extension from the catalog like Translate Text and click on Install.\nScreenshot of Firebase extension Translate Text by author\nYou might be asked to select the desired Firebase project. Afterward, a wizard will guide you through the process.\nSet up billing\nInformation about the basic costs of the extension. You might be required to upgrade your billing plan to use extensions.\nScreenshot of Firebase extensions setup process by author\nReview APIs enabled and resources created\nInformation about what other services and resources are required by the extension. You might be required to enable additional services in this step.\nScreenshot of Firebase extensions setup process by author\nReview access granted to this extension\nInformation about access rights needed by the extension.\nScreenshot of Firebase extensions setup process by author\nConfigure extension\nConfigure required settings of the extension\nScreenshot of Firebase extensions setup process by author\nIt will take some time until the extension is installed and ready.\nScreenshot of Firebase extension install process by author\nTo see all installed extensions in your project, go to Build → Extensions in your Firebase console. You can also manage your extensions from this view.\nInteresting extensions Make Payments with Google Pay\nMakes a payment with Google Pay via one or more supported Payment Service Providers, and writes the response to Cloud Firestore.\nTrigger Email\nComposes and sends an email based on the contents of a document written to a specified Cloud Firestore collection.\nSend Messages with Twilio\nSends a message using the Twilio API based on the contents of a document written to a specified Cloud Firestore collection.\nValidate Addresses with ShipEngine\nValidates an address based on the contents of a document written to a specified Cloud Firestore collection.\nAnalyze Comment Toxicity with Perspective API\nAnalyze the perceived impact the text of a comment might have on a conversation using Perspective API.\nShorten URLs\nShortens URLs written to a specified Cloud Firestore collection (uses Bitly).\nTranslate Text\nTranslates strings written to a Cloud Firestore collection into multiple languages (uses Cloud Translation API).\nResize Images\nResizes images uploaded to Cloud Storage to a specified size, and optionally keeps or deletes the original image.\nImage Processing API\nUse this extension to optimize and transform images via a powerful HTTP API with over 30 different image operations to enhance and manipulate your images.\nConclusion In this article, you saw how easy it is to enhance Firebase with new features by installing extensions.\n","permalink":"https://quickcoder.org/posts/firebase-extensions/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eFirebase allows installing various extensions to further increase functionality. Here is how to add new Firebase features with extensions. Instead of writing everything from scratch, you can pay some cents and use a plug and play extension from Firebase!\u003c/p\u003e\n\u003cp\u003eFirebase Extensions is an easy way to enhance Firebase and your app with additional capabilities without having to implement them yourself. They help with \u003ca href=\"https://extensions.dev/extensions/firebase/firestore-send-email\"\u003esending emails\u003c/a\u003e, \u003ca href=\"https://extensions.dev/extensions/firebase/firestore-translate-text\"\u003etranslating text\u003c/a\u003e, \u003ca href=\"https://extensions.dev/extensions/firebase/storage-resize-images\"\u003eresizing images\u003c/a\u003e, \u003ca href=\"https://extensions.dev/extensions/google-pay/make-payment\"\u003emaking payments\u003c/a\u003e, and other stuff. The possibilities are huge and it takes only minutes to set up these extensions. Instead of investing hours of development work to integrate 3rd parties into your app, look for an extension and save a lot of time!\u003c/p\u003e","title":"How To Add New Firebase Features With Extensions"},{"content":"Introduction Here is how to control apps with Firebase Remote Config without the need to release a new update for your apps. You can also do A/B testing and create feature flags with this service!\nWouldn’t it be great if you had some control over your apps after they are distributed via a store? You could disable a feature that still has flaws. Or your users could be part of a limited group of A/B testers. Or you might want to show a different ad banner in your app without releasing a new version. All these scenarios (and many more) are possible with Firebase Remote Config. I’ll show you how to set it up in Firebase and your app code. There will be a complete code example available at the end. Here is how to control apps with Firebase Remote Config from the backend.\nWhat is Firebase Remote Config? Firebase Remote Config lets you define parameter values in the Firebase Console. The supported data types are String, Boolean, Number, and JSON. Your app fetches these values and handles them by enabling or disabling a feature, for example. It gives you the possibility to change the behavior of your app without a new release.\n❗Warning\nDo not store any secrets or confidential data in Firebase Remote Config!\nApp setup First, install the Firebase Remote Config package into your application with flutter pub add firebase_remote_config. In a later section, we’ll talk about conditions and targeting of specific user groups and app instances. This requires Firebase Analytics to be installed, too (flutter pub add firebase_analytics). That’s it ?\nYou can then define the connection settings with the backend. A good starting point is the main function after Firebase is initialized. We also need to define default values. If the Firebase backend is not reachable (e. g. device is not connected to the internet), these values will be used instead. Always make sure your app runs in such error cases, too.\nTo get remote values, use the provided functions getBool(), getDouble(), getInt(), or getString(). Use these values to trigger a special behavior in your app. We will later define a string with the key platformString in Firebase. To get the value, the following code can be used (use a StatefulWidget and its initState function).\nYour app code is complete. In the next section, we’ll define the values that are to be downloaded to your app in the Firebase Console.\nCreate parameters with conditions in Firebase Log into your Firebase account at https://console.firebase.google.com.\nGo to Remote Config in the Build menu.\nScreenshot of Build menu in Firebase by author\n🔔 Hint\nMake sure to enable Google Analytics for your Firebase project if you haven’t done it already during the initial setup. The Firebase Console will notify you in case it is not activated yet (see image below).\nFirebase notification in case Google Analytics is not activated for the current Firebase project. To enable it, click the link and follow the instructions. Screenshot by author.\nClick in Create Configuration next.\nScreenshot of Remote Config menu in Firebase by author\nNow, you can create parameters and conditions. Conditions are a mechanism to identify groups of app users with common properties. You can provide different values to different groups based on conditions. iOS users might get a grey app background while Android users will have a blue background, for example. Follow these steps to create a parameter with conditions.\nGo to the Conditions tab and click on Add condition. Select a name and define a condition. In this example, I created a condition that applies to all Android users.\nScreenshot of the dialog to add or edit a condition for Remote Config in Firebase by author\nIn total, I created 3 conditions for Android, iOS, and web. It should look like in the image below.\nScreenshot of created conditions for Remote Config in Firebase by author\nLet’s move on to the Parameters tab to create a parameter with our conditions. Click on Add parameter to create a new one. The key is platformString and it will be used by our app code to fetch the value. Add 3 conditional values and assign the previously created conditions to them. You can define output values or rely on in-app defaults if they are set. Have a look at the following image about how to create a parameter.\nScreenshot of parameter creation for Remote Config in Firebase by author\nAfter a click on Save, you should see something like this in your overview.\nScreenshot of parameter overview for Remote Config in Firebase by author\nAn app using this parameter will output “Hello from Android!” on Android phones, “Hello from iOS!” on iPhones, and “Hello from Web!” on the web platform. On any other platform, the in-app default value will be used (we set it to “Hello!” in the previous section).\nWhen confirming the changes, they will be available immediately. But users will have to wait until their fetch interval is reset until they receive new values (see the section about fetch intervals).\nScreenshot of unsaved changes hint banner in Firebase by author\nLet’s verify the correct functionality by running our app. You should see results like in the image below.\nScreenshot of Android output (left) and Web output (right) in a demo application using Firebase Remote Config by author\nCongratulations, your Firebase Remote Config setup is completed and working! ?\nFetch intervals The minimumFetchInterval property of the RemoteConfigSettings object defines the period in which follow-up calls to Firebase Remote Config will be ignored. There is a server-side limit, so setting the value too low might result in reaching that limit very quickly with many users. The default value is 12 hours but it makes sense to use lower values like 1 minute during development. Just make sure to only use it in development mode.\nLoading strategies The Firebase docs recommend 3 different loading strategies.\nFetch and use\nDownload the values and update the relevant app parts immediately. This is only suitable if there are no big UI changes involved that could confuse users while they are using the app.\nFetch and use with loading screen\nDisplay a loading screen and download the values in the background. This is useful if the values might drastically change the UI or show/hide essential features. Make sure to discard the loading screen after a certain time period even if the download hasn’t finished yet. Users usually don’t accept long app startup times.\nFetch and use with next app start\nActivate previously fetched values on app start. Run an asynchronous call to Firebase Remote Config while the user is working with your app but don’t activate the fetched results. When the user starts the app for the next time, the fetched values will be activated.\nAdvanced options There is a Remote Config REST API that you can use to add, delete, or modify values and templates in Firebase Remote Config without using the Firebase UI. If you are developing an additional tool to manage your A/B tests, you could use the REST API with your tool to change parameters for user groups for example. The Firebase docs provide a good overview to get started with.\nConclusion In this article, you learned how to control apps with Firebase Remote Config without the need to release a new update for your apps. Firebase Remote Config provides an easy way to control your app even without a new release. You can load data at runtime and trigger a special behavior in your app.\n","permalink":"https://quickcoder.org/posts/firebase-remote-config/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere is how to control apps with Firebase Remote Config without the need to release a new update for your apps. You can also do A/B testing and create feature flags with this service!\u003c/p\u003e\n\u003cp\u003eWouldn’t it be great if you had some control over your apps after they are distributed via a store? You could disable a feature that still has flaws. Or your users could be part of a limited group of A/B testers. Or you might want to show a different ad banner in your app without releasing a new version. All these scenarios (\u003ca href=\"https://firebase.google.com/docs/remote-config/use-cases\"\u003eand many more\u003c/a\u003e) are possible with Firebase Remote Config. I’ll show you how to set it up in Firebase and your app code. There will be a complete code example available at the end. Here is how to control apps with Firebase Remote Config from the backend.\u003c/p\u003e","title":"How To Control Apps With Firebase Remote Config"},{"content":"Introduction In this article, I’ll show you how to easily use Firebase Storage from your Flutter app to read and write data. Make your app cloud-ready with this guide!\nFirebase Storage is an object storage provider in the cloud to store and serve data. Possible scenarios include profile pictures for your users or documents like PDF files that are uploaded during a registration process. In this article, I’ll show you how to set it up, read and write data, and implement basic access control rules. Here is a list of the topics:\nSet up Firebase Storage Write data Read data Delete data Monitoring space consumption Access control Firestore vs Storage CORS fix for web You will need to set up a Firebase project by yourself to use these code examples here or the one from my GitHub page.\nSet up Firebase storage The first step is to install the package firebase_storage. After that, you need to set up a bucket where your data is stored. Think of a bucket as a root folder in a file system. This can be done via the Firebase Console dashboard.\nGo to Storage → Get Started\nScreenshot of Firebase Storage setup by author\nSelect Start in test mode and click Next (we will come back to access control later)\nScreenshot of Firebase Storage access control setup by author\nChoose your desired location and confirm with Done. To get an overview of locations and what their benefits are, click here. I recommend choosing the location of your target user audience if possible.\nScreenshot of Firebase Storage location setup by author\nA default bucket will be created where you can store your data. It is also possible to add further buckets.\nSetup complete, let’s play with some data!\nStorageReference The first step is to get a StorageReference object pointing to a file (item) or directory (prefix) in our bucket. You can navigate through a reference tree with the child() method and the root and parent properties. Google Cloud Storage URIs and HTTPS URLs are also supported when creating references.\nRead data Let’s assume we have a bunch of files stored in our bucket at the root level and we want to download them. The following code grabs 5 items found at the storage reference, downloads the content, and reads the metadata object.\nTo control how many results are grabbed, you can use the ListOptions object. There is a maxResults and a pageToken property. getData() fetches the content as a byte array, getMetadata() gets enhanced file information, and getDownloadURL() returns a string that points to the file (or folder). If you want to handle folders, use files.prefixes instead of files.items.\nWrite data But how do we get data into our storage bucket? The first way is to use the dashboard. There are buttons to upload files and create folders.\nScreenshot of Firebase Storage dashboard by author\nHowever, as we are programmers, we like to do things with code. And here is a quick example of how to upload a file.\nWe use a Reference and the putFile() method to upload a File object. targetName can also be a path with a file name, it will then be created at the corresponding subfolder location. FilePickers are most often used to get files, but you can also create and write them yourself.\nThe putFile() method returns an UploadTask object that allows us to monitor and control the upload process.\nThis example listens to the event stream and updates a progress label while the task is in progress. The UploadTask object also has methods to pause, resume, or cancel the action. For longer-running operations, you have quite the tools to notify the user of what’s going on.\nDelete data Deleting is very simple. Get a reference to an item (file) or prefix (folder) and call the delete() method. Pretty easy, hm?\nMonitoring space consumption To monitor your current space consumption, head over to your Firebase Storage Usage dashboard. The data is updated daily and you get a free tier before any costs arise.\nScreenshot of Firebase Storage usage dashboard by author\nYou might be wondering why there is already so much space occupied though you haven’t done anything with the database yet. A probable answer is that you deployed one or more cloud functions. You can’t see these buckets in Firebase, they are only visible if you use the Google Cloud Console. Don’t delete these buckets, if you have active cloud functions!\nClick here for more details on why you could be charged some cents though everything seems within the free quota of Firebase.\nAccess control I already wrote about it in my article covering Cloud Firestore. Here is a little recap since the pattern is identical.\nAccess control ensures that only those operations happen on the data that are supposed to happen. There are various options to configure rules, but I’ll give only a brief introduction. For further details, refer to the Firebase documentation or the following two articles.\nBasic examples of using Cloud Firestore Security Rules Advanced examples of using Cloud Firestore Security Rules Your current rule will look similar to this:\nrules_version = \u0026#39;2\u0026#39;; service firebase.storage { match /b/{bucket}/o { match /{allPaths=**} { allow read, write: if false; } } } The first line identifies the used service because rules can also apply to Cloud Firestore and Realtime Database. The following match statement(s) identify for which path(s) the rule should be enforced. You can nest multiple match statements or just use one. The allow statement tells us under which condition(s) access is allowed. After the allow keyword, you can add multiple methods like read and write. The following methods are available:\nget — read single documents list — read queries create — write new documents update — write existing documents delete — delete data read — get + list write — create + update match statements often use wildcards. Text in curly brackets like {bucket} is a single-segment wildcard and can be used in conditions, for example. To make the wildcard recursive, add two star characters {allPaths=**}.\nThe rule above denies all read and write access to all paths in all buckets.\nHere are some common rule conditions that you can use:\nallow read, write: if false; // no access allow read, write: if request.auth != null; // access when authenticated match ... { allow read: if true; // read access for everyone allow write: if request.auth != null; // write access when authenticated } A good rule to start with should check for authentication. That way, you will prevent any other party than your app from accessing the data. If your application gets bigger, the rules also get more complex.\nFirestore vs Storage When to use what?\nFirebase Cloud Firestore is a NoSQL database intended for storing data like strings, numbers, bools, or complex JSON objects. Firebase Storage is intended to store bigger data chunks like images, PDF files, videos, or user-generated content in general.\nTo store an image in Firestore, you’d probably try to convert it into a Base64 string and then insert it into the database. But Firestore has limits that prevent these approaches. So don’t bother trying and just go with Storage.\nCORS fix for web If you are on the web platform, downloads won’t work because of a CORS error. To fix this, you need to install the Google Cloud CLI and set a CORS rule for your bucket. Create a file cors.json and fill it with the following content:\n[ { \u0026#34;origin\u0026#34;: [\u0026#34;*\u0026#34;], \u0026#34;method\u0026#34;: [\u0026#34;GET\u0026#34;], \u0026#34;maxAgeSeconds\u0026#34;: 3600 } ] To deploy, use the following command:\ngsutil cors set cors.json gs://\u0026lt;your-cloud-storage-bucket\u0026gt;\nAfter that, the error won’t occur again. For more details, read the documentation.\nConclusion Firebase Storage is a fast, easy, and simple way to allow your users to store their content. With this article, you should know how to easily use Firebase Storage from your Flutter app to read and write data. Here is a short demo video of the source code.\nFirebase Cloud Storage demo by author\n","permalink":"https://quickcoder.org/posts/firebase-storage/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eIn this article, I’ll show you how to easily use Firebase Storage from your Flutter app to read and write data. Make your app cloud-ready with this guide!\u003c/p\u003e\n\u003cp\u003eFirebase Storage is an object storage provider in the cloud to store and serve data. Possible scenarios include profile pictures for your users or documents like PDF files that are uploaded during a registration process. In this article, I’ll show you how to set it up, read and write data, and implement basic access control rules. Here is a list of the topics:\u003c/p\u003e","title":"How To Easily Use Firebase Storage From Your Flutter App"},{"content":"Introduction Here is how to host your Flutter web app with Firebase Hosting. This article guides you through all the steps needed. This service is free of charge!\nFlutter apps are available for various platforms like Android, iOS, desktop systems, and also the web. With Firebase Hosting, there is an easy tool to deploy your Flutter web app and access it in a browser. This is a simple way to distribute apps to testers or automate the release process of new versions. Here is how to host your Flutter web app with Firebase Hosting.\nOpen any command line tool (PowerShell, cmd, bash, Visual Studio terminal, …) and navigate to your Flutter project. Then, execute the command firebase init hosting.\nScreenshot of the initialization of Firebase Hosting by author\nYou will be asked several questions. Answer them like me in the image above to get a feeling of the workflow. Set your public directory to build/web because this is the location where the Flutter framework will put the files for the web app. This will then be deployed to Firebase Hosting.\nScreenshot of the build process of a Flutter web app by author\nRun the command flutter build web to create the web app.\nScreenshot of the deployment of the Flutter web app to Firebase Hosting by author\nAfterward, use the command firebase deploy --only hosting to publish the web app to Firebase Hosting.\nScreenshot of the deployed Flutter web app with Firebase Hosting by author\nThe Hosting URL of the command-line output points to the location where the web app is running. Open it in a browser and verify that everything works as intended. If you use the provided demo application, you should see something like the image above.\nTo manage your releases, you can use the Firebase Hosting dashboard.\nIn the Build menu, go to Hosting.\nScreenshot of the Hosting menu in Firebase by author\nYou can see the deployed version in the release overview.\nScreenshot of release overview in Firebase Console by author\nThat’s it. Your web app is published and can be managed with Firebase Hosting! ?\nHow can I deploy to a preview channel? Follow the steps describe before but when deploying, use the command firebase hosting:channel:deploy CHANNEL_NAME to push your deployment to Firebase. A new channel will appear in the Hosting dashboard. You can customize its storage settings and expiration time by clicking on the date button. By default, the channel will be deleted after 7 days and store 10 releases at most.\nScreenshot of a preview channel in Firebase Hosting by author\nHow can I delete a previous release to free up storage space? In Firebase Console, go to Hosting and look for the desired deployment in your release history. Click the three dots on the right and select Delete. This will schedule a deletion of the deployed contents within 24 hours. However, the table entry will remain.\nYou can only delete previous releases but not the active one.\nHow can I change Firebase Hosting settings? You can either rerun the firebase init hosting command or edit the firebase.json file in your repository manually. In any case, you have to follow up with the command firebase deploy --only hosting to publish the changes.\nConclusion In this article, we learned how to host your Flutter web app with Firebase Hosting. You can find an example app on my GitHub page. Deploy it to your Firebase project to test.\n","permalink":"https://quickcoder.org/posts/firebase-hosting/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere is how to host your Flutter web app with Firebase Hosting. This article guides you through all the steps needed. This service is free of charge!\u003c/p\u003e\n\u003cp\u003eFlutter apps are available for various platforms like Android, iOS, desktop systems, and also the web. With Firebase Hosting, there is an easy tool to deploy your Flutter web app and access it in a browser. This is a simple way to distribute apps to testers or automate the release process of new versions. Here is how to host your Flutter web app with Firebase Hosting.\u003c/p\u003e","title":"How To Host Your Flutter Web App With Firebase Hosting"},{"content":"Introduction Here is how how to secure your Firebase backend with App Check. I’ll demonstrate the setup with a Flutter app. Don\u0026rsquo;t let intruders make your bill go crazy!\nSecuring your backend saves you a lot of trouble. Think about data loss or a huge bill at the end of the moment. Firebase provides its service App Check to greatly reduce the possibility of an attacker being successful. Here is how how to secure your Firebase backend with App Check.\nHow does App Check work? Firebase App Check uses 3rd party attestation providers like DeviceCheck (Apple), Play Integrity (Android), or reCAPTCHA v3 (Web) to ensure that only validated requests will be handled by the backend. Your app calls the attestation provider and receives an attestation that will be sent to the App Check servers. App Check verifies the attestation and returns a token. The token will be attached to every request by your app. If the token expires, the verification process is restarted.\nAll this happens behind the scenes and except for some initial configuration, you don’t have to do anything. ?\nWhat Firebase services are covered by App Check? The following services are currently supported by App Check:\nRealtime Database Cloud Firestore Cloud Storage Cloud Functions (only for callable functions) In previous articles, I already talked about Cloud Firestore, Cloud Storage, and Cloud Functions. If you integrated one of these services, you can use App Check as well.\nApp Check is also available to secure your own non-Firebase backend. As this is beyond the scope of this article, interested readers can have a look at the App Check documentation for details.\nHow to enable an attestation provider? We will have a look at the attestation provider Play Integrity. The other providers work in a similar way.\n❗ Warning\nUnfortunately, Play Integrity only works for apps that are distributed via Google Play. If that\u0026rsquo;s not the case for your app, you cannot use it. The setup will work, but all requests from your app to the backend will fail.\nFirst, open the App Check menu in your Firebase Console.\nScreenshot of Firebase Console menu by author\nThen select the app for which you want to enable App Check by clicking the Register button and selecting an attestation provider.\nScreenshot of Firebase App Check app selection by author\nSelect Play Integrity as the provider. Then, insert your SHA256 fingerprint key. To generate the key, follow the instructions on this page. You’ll need to execute the command-line program keytool program that is part of the JAVA Runtime Environment. Click the Save button when you are done.\nScreenshot of Play Integrity setup in Firebase by author\nAfterward, you’ll see the successful registration of Play Integrity.\nScreenshot of completed Play Integrity setup in Firebase by author\n💡 Token time to live considerations\n* A shorter time interval reduces the time for an attacker to abuse leaked or cracked tokens.\n** A shorter time interval requires more frequent App Check token renewals which cost time\n*** A shorter time interval requires more frequent attestation provider calls that deplete the free quota faster\nHow to enforce App Check for specific services After you added your attestation provider, you need to enforce App Check for your desired services to finally activate it in Firebase. It will take around 15 minutes until the process is finished. Afterward, only apps with proper App Check setup can access your secured backend services.\nGo to the APIs tab and select a service, for example, Storage.\nScreenshot of APIs tab in Firebase by author\nScreenshot of App Check request overview for Storage in Firebase by author\nClick the Enforce button and after around 15 minutes, only registered apps will be able to access your Firebase Storage backend.\nScreenshot of enforced Storage API in Firebase by author\nThat’s it. Your backend is secured!\n💡 Tip\nMake sure to register all projects of your app with App Check. It doesn’t make sense to just register the Android app because then, your iOS app users won’t be able to access the Firebase backend!\nHow to set up App Check in a Flutter app? Follow these few steps to allow a Flutter app to access App Check secure Firebase backend services.\nInstall the package firebase_app_check. Add the initialization code in your main() function. Make sure, FirebaseAppCheck is initialized AFTER Firebase.\nAnd that’s it! Your Firebase App Check dashboard should monitor incoming requests from now on.\nI already use Firebase Authentication. Why do I need App Check? Authentication is to identify users, App Check is to identify apps or devices. Here is a little example of the difference:\nImagine you have created an app with a members area where every user has its own login credentials.\nIf user A logs in, he/she can just edit user A’s data. If user B logs in, he/she can just edit user B’s data.\nBut they both can access the backend because the app passed App Check’s validation routine. If App Check denies access, no user can access his or her data.\nMy app is already published. Can I still integrate App Check? Publish a new app version with the App Check code included, but don’t enable App Check in Firebase Console yet. You need to give the users time to get the update. Otherwise, you’d be excluding users with an old app version from the moment you enable App Check for the project. You can monitor the adaptation progress in the App Check menu. A high percentage of Verified requests is a good indicator that the users got the update.\nScreenshot of Firebase App Check request metrics from Google Firebase\nCome on! Do I really need App Check? Well, it depends. Is it just a private app you are developing for yourself or a small audience? Then you could probably skip it. But as soon as you start making serious money with your app, you should definitely consider improving your security measures. It can take just one incident to make your Firebase bill go crazy because someone is constantly accessing your resources. Or think about the loss of vital data that cannot be recovered easily. Afterward, you’ll be furious that you didn’t spend 30 minutes to an hour setting up App Check or another security protocol.\nJust ask yourself these questions:\nWhat is the worst case? What will it cost to recover from the worst case? Will it be cheaper to implement security measures upfront? Then, you’ll know the answer if you need App Check ?\nConclusion Better safe than sorry. App Check protects your backend with an additional security layer to minimize the risk of data loss or a huge bill at the end of the month. Consider using it if you value your backend resources and your nerves. In case of an attack, you’ll be glad that you invested one single hour for protection mechanisms.\n","permalink":"https://quickcoder.org/posts/firebase-app-check/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere is how how to secure your Firebase backend with App Check. I’ll demonstrate the setup with a Flutter app. Don\u0026rsquo;t let intruders make your bill go crazy!\u003c/p\u003e\n\u003cp\u003eSecuring your backend saves you a lot of trouble. Think about data loss or a huge bill at the end of the moment. Firebase provides its service App Check to greatly reduce the possibility of an attacker being successful. Here is how how to secure your Firebase backend with App Check.\u003c/p\u003e","title":"How To Secure Your Firebase Backend With App Check"},{"content":"Introduction In this article, I will demonstrate how to work with Firebase Cloud Functions in Flutter apps in the most simple way. This is your entry into the world of cloud computing!\nFirebase Cloud Functions is a service from Google that enables you to execute code in the cloud. A major benefit is scalability. If your app has many users, you won’t have a bottleneck when using Firebase. I will show you how to work with Firebase Cloud Functions in Flutter apps in the most simple way. The following sub-topics will be covered in this article:\nSetting up Firebase and your app Initializing cloud functions Writing a cloud function Deploying a cloud function Calling a cloud function Functions as HTTP request endpoints Scheduled functions Regions Logging Setting up Firebase and your app At first, you need to set up Firebase and connect it with your app.\nThe next step is to install the Firebase Cloud Functions package. Add it to your pubspec.yaml or use the command line. If you need further instructions, read the installation manual.\nThe setup is completed. You can now start writing your functions.\nInitializing cloud functions Log in with Firebase by running the command firebase login. Afterward, run the command firebase init functions from your project root directory. The tool will ask some questions that you need to answer. I’ll give you an answer workflow example.\nAre you ready to proceed? → Yes Please select an option: → Use an existing project What language would you like to use to write Cloud Functions? → JavaScript Do you want to use ESLint to catch probable bugs and enforce style? → No Do you want to install dependencies with npm now? → Yes After successful completion, you will find a new functions folder in your project structure.\nScreenshot of functions folder after initialization by author\nThe next step is to write the actual code that will be executed in the cloud.\nWriting a cloud function Our demonstration code will be a function that expects a string as an argument and converts the input into upper case. It’s very simple but it should give you an idea of what you can do and how you can achieve it.\nWe open the index.js in the functions folder and write the following code:\nWe define a callable method called toUpperCase with two parameters. data contains the string we want to transform and the second parameter is a CallableContext that we won’t need in this example. Then we use the built-in toUpperCase method of JavaScript and return the result. Easy as that!\n💡Tip\nYour code should be idempotent! This means it produces the same result even when it is invoked multiple times. Make sure to stick to that pattern because it will allow you to retry failed executions very easily.\n💡Tip\nYou can also use TypeScript, Python, or Dart as a programming language.\nDeploying a cloud function Deployment is pretty simple, just run the command firebase deploy --only functions and wait for the process to finish. It can take up to 5 minutes till your dashboard is updated. The dashboard also provides information about logs and the utilization of your functions.\nScreenshot of Firebase functions dashboard by author\nCalling a cloud function A callable function can be identified by the onCall((request) =\u0026gt; {...}); method. The Firebase dashboard doesn’t distinguish between callable functions and HTTP endpoints, both triggers show “Request” as a type. To call a function with the name toUpperCase(), have a look at the following code example:\nHTTP request endpoint With the code onRequest((request, result) ⇒ {...}); you can define an HTTP endpoint that other web services can call on demand. That way you can define interfaces for 3rd party software to operate with your data or logic. You don’t even need the Firebase package to call endpoints, but something to create requests like http or Dio. Have a look at the example code:\n💡 Tip\nYou need to replace the link to the function with your deployed version!\n❗Warning\nA HTTP endpoint can be accessed by anyone by default. Make sure to implement safety measures like an authorization token. Click here for an official example of how to secure your endpoints.\nScheduling a function It is also possible to schedule the execution of a function. The interval or timeframe can either be described with the App Engine cron.yaml syntax or with the good old Unix crontab syntax. The following code example defines a function that is executed every 10 minutes.\nThe next example works with Unix crontab syntax and defines a timezone. Click here for a list of all supported timezones.\nRegions Firebase uses different regions that affect pricing and latency. You can specify your desired region with the region property and a valid region name as a parameter. It works similar to the time zone and schedule properties in the earlier code examples. Example: region: ”europe-west3”. Click here for a list of all available regions. I recommend using the nearest region to your location. You can also pass multiple regions separated by commas to the function.\nLogging To monitor the execution of functions, you can use logging. The output will be visible in your Firebase Functions dashboard.\nconst { log, info, debug, warn, error, write } = require(\u0026#34;firebase-functions/logger\u0026#34;); log(\u0026#34;log:\u0026#34;, someObj); info(\u0026#34;info:\u0026#34;, someObj); debug(\u0026#34;debug:\u0026#34;, someObj); warn(\u0026#34;warn:\u0026#34;, someObj); error(\u0026#34;error:\u0026#34;, someObj); write(\u0026#34;write:\u0026#34;, someObj); Further details about logging can be found in the official documentation.\nonCall() or onRequest()? Callable functions and HTTP functions are quite similar. A callable function is an HTTP function with special request parameters. The main differences are that authentication tokens for Firebase and Firebase Cloud Messaging as well as App Check tokens are automatically included in the request if available. In addition, a callable function validates these tokens by default. This means you get authentication features for free if you use the onCall() method of the Cloud Functions package.\nAs stated before, HTTP functions don’t have any access control features integrated by default. You have to do it on your own. But it is also true that you can execute a callable function with an HTTP request if you provide the correct data. For further details, read the documentation here.\n✅ Go with callable functions if your app is the only consumer\n✅ Go with HTTP functions if you have 3rd party consumers (for example a public REST API) but make sure to implement access control and other security mechanisms.\nConclusion In this article, I showed you how to work with Firebase Cloud Functions in Flutter apps in the most simple way. Firebase Cloud Functions provide an easy way to use external computation power in your Flutter app. Here is a short demo video of the source code.\nFirebase Cloud Functions demo by author\n","permalink":"https://quickcoder.org/posts/firebase-functions/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eIn this article, I will demonstrate how to work with Firebase Cloud Functions in Flutter apps in the most simple way. This is your entry into the world of cloud computing!\u003c/p\u003e\n\u003cp\u003eFirebase Cloud Functions is a service from Google that enables you to execute code in the cloud. A major benefit is scalability. If your app has many users, you won’t have a bottleneck when using Firebase. I will show you how to work with Firebase Cloud Functions in Flutter apps in the most simple way. The following sub-topics will be covered in this article:\u003c/p\u003e","title":"How To Work With Firebase Cloud Functions In Flutter Apps"},{"content":"Introduction This short article shows how to create a new Flutter project in 3 different ways, so you can start working on your app. Here is how to do it with Visual Studio Code, Android Studio, and the command line.\nI will show you three ways how to create a new Flutter project. You can do it with the IDEs Visual Studio Code or Android Studio or by using the command-line tool from the Flutter SDK. You need to have the Flutter SDK installed, otherwise non of these tips will work. To install the Flutter SDK, see the Flutter homepage.\nVisual Studio Code Go to View ▶ Command Palette… Open command palette in VSCode\nEnter or select Flutter: New Project Choose command in command palette\nSelect the project template Application. Select template for new Flutter project\nFollow the guide until the end Your project will be created at the desired location and Visual Studio Code will open it immediately.\nAndroid Studio Go to File ▶ New ▶ New Flutter Project… Create new Flutter project in Android Studio\nMake sure the Flutter SDK path is found and correct. Make sure Flutter SDK install directory is found in Android Studio\nDefine additional project settings and click Finish. Enter project details for the new Flutter project in Android Studio\nYour project will be created at the desired location and Android Studio will open it immediately.\nCommand-line Open any command-line tool (like CMD on Windows)\nRun the command flutter create testproject. It will create a folder called testproject at the current location and put all the app code inside this folder.\nCreate a new Flutter project with the Flutter command-line tool\nTo customize your new project, see flutter create --help. There are many options to set for your project. However, the default settings should be fine for most of the cases. Conclusion With this guide, you should be able to create new Flutter projects and start developing your apps. For more information about packages go to the official Flutter documentation.\n","permalink":"https://quickcoder.org/posts/flutter-create-project/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eThis short article shows how to create a new Flutter project in 3 different ways, so you can start working on your app. Here is how to do it with Visual Studio Code, Android Studio, and the command line.\u003c/p\u003e\n\u003cp\u003eI will show you three ways how to create a new Flutter project. You can do it with the IDEs \u003ca href=\"https://code.visualstudio.com/\"\u003eVisual Studio Code\u003c/a\u003e or \u003ca href=\"https://developer.android.com/studio\"\u003eAndroid Studio\u003c/a\u003e or by using the command-line tool from the Flutter SDK. You need to have the Flutter SDK installed, otherwise non of these tips will work. To install the Flutter SDK, see the \u003ca href=\"https://docs.flutter.dev/get-started/install\"\u003eFlutter homepage\u003c/a\u003e.\u003c/p\u003e","title":"How To Create A New Flutter Project"},{"content":"Introduction In this article, we are going to implement navigation logic and learn how to navigate between pages in your Flutter application. Create multi-page apps without any effort!\nIf you don’t want to develop a single-page application, you will need a navigation pattern to get from one page to another. In a Flutter application, there are 3 different ways to handle this problem. I’ll show them to you in the following sections. Let\u0026rsquo;s see how to navigate between pages in your Flutter application!\nExample app navigation between pages\nThe Navigator object The Navigator object exposes methods to transition from one page to another within a Flutter application. It behaves like a stack data structure which means that the last element added will be the first element removed. To access the Navigator you can call Navigator.of(context) where context represents the current BuildContext. I’ll demonstrate it in the next sections.\nThe basic way — push and pop The Navigator behaves like a stack, so the most simple way is to push a new page onto it when you want to navigate to it and to pop a page if you want to go back. The following code demonstrates how to navigate to a new page.\nTo go back, you simply call Navigator.of(context).pop() and you’re back at the previous page. To check if back navigation is possible, there is the method canPop() which returns false if there aren’t any more pages on the stack to be popped. The hardware or software back button of your mobile device will also trigger the pop() method if possible.\n❗Warning\nIf you call pop() and there is only the current page on the stack, the application will be closed.\nTo skip the page history entirely, you can also use the pushReplacement() method. It will replace the current page with the given one so that there is only the given page on the stack. But the user won’t be able to use the back button.\nThe diagram shows the stack behavior once again:\nThe advanced way — predefined routes Predefined routes are a good choice if you have a smaller amount of pages that don’t require any arguments to be passed in. To configure the routes, you need to fill the routes property in the MaterialApp object. A simple example is below:\nIn this case, you need to use the pushNamed() or pushReplacementNamed() methods of the Navigator object. So for example when navigating to the login page which has a predefined route /login, you call Navigator.of(context).pushNamed(”/login”). It is recommended to define constants for the routes to reduce possible spelling errors.\n❗Warning\nDon’t forget the initialRoute property or add an onUnknownRoute handler. Otherwise, the app won’t start.\nThe expert way — route generation If you don’t use the routes table, but want to go with named routes, you can add a handler to the onGenerateRoute function in the MaterialApp object. You can see an example of that below:\nEvery time when a route is pushed via the Navigator, this handler function is executed to find the correct page to navigate to. The name property contains the passed route. The big advantage is that you can handle navigation arguments with this approach. They are passed via the arguments property. It gives you more control than the static approach with predefined route tables.\nSending data to another page Passing data to a page is pretty simple. When using the push-pop approach without named routes, you just need to add the data as a constructor argument.\nNavigator.of(context).push(MaterialPageRoute(builder: (context) =\u0026gt; Login(data: \u0026#34;dummy\u0026#34;))); When using named routes approach, you can use the arguments parameter of the pushNamed method to pass any data you want.\nNavigator.of(context).pushNamed(”/login”, “dummy”); The parameter is of the type Object, so you can pass anything you want from simple strings over maps to custom objects. The target page is responsible for parsing or casting the data. Have a look at the paragraph about route generation above. If you are using a predefined routes table, you cannot pass any data at runtime.\nThe pop() method can also pass data. Therefore, the caller needs to wait for the back navigation and read the passed information. Just call Navigator.of(context).pop(”dummy”); to pass a string to the caller. The caller should await the return of the data.\nvar res = await Navigator.of(context).pushNamed(\u0026#34;/login\u0026#34;); // \u0026lt;-- wait for pop() print(res as string); // prints \u0026#34;dummy\u0026#34; Conclusion In this article, we had a look at navigation in a Flutter application and learned how to navigate between pages in your Flutter application. Either with push and pop, with named routes, or with dynamic route generation, there are many possible ways for this task.\nYou can find the source code on GitHub.\n","permalink":"https://quickcoder.org/posts/flutter-navigation/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eIn this article, we are going to implement navigation logic and learn how to navigate between pages in your Flutter application. Create multi-page apps without any effort!\u003c/p\u003e\n\u003cp\u003eIf you don’t want to develop a single-page application, you will need a navigation pattern to get from one page to another. In a Flutter application, there are 3 different ways to handle this problem. I’ll show them to you in the following sections. Let\u0026rsquo;s see how to navigate between pages in your Flutter application!\u003c/p\u003e","title":"How To Navigate Between Pages In Your Flutter Application"},{"content":"Introduction Learn how to prevent RichText styling issues in Flutter apps and consider using the alternative Text.rich. This small tip will save you time!\nFlutter provides developers with a versatile set of widgets for creating rich and engaging user interfaces. Two commonly used widgets for displaying styled text are RichText and Text.rich. While both widgets offer text styling capabilities, it\u0026rsquo;s essential to understand their differences, especially the fact that RichText does not use the default text theme. Learn how to prevent RichText styling issues in Flutter to not get headaches like did.\nRichText Widget The RichText widget in Flutter is designed to display a paragraph of mixed-style text. It allows you to apply different text styles to different parts of the text within a single widget. This makes it an excellent choice for situations where you need to format text in various ways within the same text block.\nOne important distinction to note is that RichText does not inherit the default text theme of your app. This means that if you want to apply a specific text style to your RichText, you need to explicitly define it for each TextSpan within the widget. While this may require a bit more work compared to using Text with the default text theme, it grants you full control over the styling of individual text spans.\nHere\u0026rsquo;s a simple example of how to use RichText:\nRichText( text: TextSpan( text: \u0026#39;This is an example of \u0026#39;, style: TextStyle(color: Colors.black), children: \u0026lt;TextSpan\u0026gt;[ TextSpan( text: \u0026#39;richly styled text\u0026#39;, style: TextStyle( fontWeight: FontWeight.bold, color: Colors.blue, ), ), TextSpan( text: \u0026#39; in Flutter.\u0026#39;, style: TextStyle( fontStyle: FontStyle.italic, ), ), ], ), ) In this example, we have three different text spans with varying styles, all displayed within the same RichText widget.\nText.rich Widget The Text.rich widget is an alternative to RichText that simplifies text styling by making use of the default text theme. Unlike RichText, Text.rich inherits the text style of your app\u0026rsquo;s theme, making it a convenient choice for maintaining consistency in your app\u0026rsquo;s design.\nHere\u0026rsquo;s how you can use Text.rich:\nText.rich( TextSpan( text: \u0026#39;This is an example of richly styled text in Flutter.\u0026#39;, children: \u0026lt;TextSpan\u0026gt;[ TextSpan( text: \u0026#39; You can apply different styles easily.\u0026#39;, style: TextStyle( fontWeight: FontWeight.bold, color: Colors.blue, ), ), ], ), ) In this example, the Text.rich widget automatically uses the default text theme\u0026rsquo;s style, but you can still override it for specific text spans as needed.\nComparison Here is the output of the code examples from above.\nFlutter RichText vs Text.rich output with styling. RichText does not use the default text theme font while Text.rich does.\nAs you can see, they use different fonts. Since we didn’t specify the fonts in the widget, I would assume that the default text theme font is applied. But this is not the case for RichText. My advice is to only use Text.rich to prevent this problem.\nI didn’t spot other properties than the font so far, but maybe there are more to be aware of. With Text.rich you should be on the safer side.\nConclusion In this article, you learned how to prevent RichText styling issues in Flutter to not get headaches like I did.\n","permalink":"https://quickcoder.org/posts/how-to-prevent-richtext-styling-issues-in-flutter/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eLearn how to prevent \u003ccode\u003eRichText\u003c/code\u003e styling issues in Flutter apps and consider using the alternative \u003ccode\u003eText.rich\u003c/code\u003e. This small tip will save you time!\u003c/p\u003e\n\u003cp\u003eFlutter provides developers with a versatile set of widgets for creating rich and engaging user interfaces. Two commonly used widgets for displaying styled text are \u003ccode\u003eRichText\u003c/code\u003e and \u003ccode\u003eText.rich\u003c/code\u003e. While both widgets offer text styling capabilities, it\u0026rsquo;s essential to understand their differences, especially the fact that \u003ccode\u003eRichText\u003c/code\u003e does not use the default text theme. Learn how to prevent RichText styling issues in Flutter to not get headaches like did.\u003c/p\u003e","title":"How To Prevent RichText Styling Issues In Flutter"},{"content":"Introduction Here is how to use Firebase Authentication in your Flutter app to authenicate your users without any effort. Don\u0026rsquo;t bother building your own authentication service and use this proven service instead!\nWhat is Firebase Authentication? Firebase Authentication is a simple way to verify a user without having to worry about storing credentials. It supports a variety of login mechanisms and is easy to set up. You receive a complete registration flow for your users including mail confirmation and validation if desired. Before implementing an authentication process by yourself, consider using this option.\nYou will need to set up a Firebase project by yourself to use these code examples here or the one from my GitHub page.\nWhich mechanisms are supported? Email/password User logs in with an email and password combination or with an email link\nPhone number User logs in and verifies with his phone number\nAnonymous User logs in anonymously for temporal access\nSocial login User logs in with a social provider like Google, Facebook, GitHub, Microsoft, Yahoo, etc. Requires additional plugins to be installed.\nHow to enable providers? On the left of your Firebase Console dashboard, go to Build → Authentication.\nAuth menu screenshot in Firebase Console dashboard by author\nThen select the tab Sign-in method.\nScreenshot of available sign-in methods by author\nAll available providers are listed here and you can activate and configure the ones you like to use. For the following examples, we will use Email/Password and Google, so they are already marked in the screenshot.\nExample: Authentication with mail and password First, we need to activate the Email/Password provider. Click on it, enable the checkbox, and confirm with Save.\nScreenshot when enabling Email/Password provider in Firebase by author\nInstall the firebase_auth package into your Flutter app if you haven’t already done so.\nTo register a new user, you can use the following code.\nThe user will also be logged in after the code is executed. The next code snippet will perform a log-out of the current user.\nAnd if you want to log in without registering as a new user, you can use the following code.\nTo handle authentication state changes, you can listen to a stream that provides updates. See the following code example.\nFirebaseAuth.instance.authStateChanges().listen((user) {...});\nThe User object will be null if you are not logged in. Otherwise, it contains data.\nAfter the registration, you’ll find the user’s details (except for the password, of course) in Firebase. You can manage users from the Users tab of the Authentication dashboard.\nHere is a short demo video of the provided source code.\nFirebase authentication demo by author\nScreenshot of users dashboard in Firebase by author\nThere are additional features that you can make use of like\nChange or reset passwords\nCustomize emails that the user will receive\nUse an emulator for testing\nExample: Authentication with Google Activate the Google sign-in provider, enter the required mail address, and confirm with a click on Save.\nScreenshot when enabling Google provider in Firebase by author\nInstall the firebase_auth package into your Flutter app if you haven’t already done so.\nUse the following code snippet to start the log-in process via Google.\nFor a log-out, you can use the same code as in the previous example.\n❗Warning\nSocial authentication with Google, Facebook, Microsoft, … only works on the web platform. On all other platforms, you’ll get a runtime exception. An example of how to get the Google sign-in on Android or iOS can be found in the following article. It requires an additional package and some setup steps.\nConclusion Firebase Authentication provides an easy way to implement a login and registration process for your app without handling all the details. In this article, you learned how to use Firebase Authentication in your Flutter app. The package is easy to use and provides understandable error messages that can be passed directly to the user. You can find the complete example source code on my GitHub page.\n","permalink":"https://quickcoder.org/posts/firebase-auth/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere is how to use Firebase Authentication in your Flutter app to authenicate your users without any effort. Don\u0026rsquo;t bother building your own authentication service and use this proven service instead!\u003c/p\u003e\n\u003ch1 id=\"what-is-firebase-authentication\"\u003eWhat is Firebase Authentication?\u003c/h1\u003e\n\u003cp\u003eFirebase Authentication is a simple way to verify a user without having to worry about storing credentials. It supports a variety of login mechanisms and is easy to set up. You receive a complete registration flow for your users including mail confirmation and validation if desired. Before implementing an authentication process by yourself, consider using this option.\u003c/p\u003e","title":"How To Use Firebase Authentication In Your Flutter App"},{"content":"Introduction Learn how to create a nice mouse hover effect in a Flutter app to make it more appealing on the desktop and web platform.\nSince Flutter is gaining more and more traction in the desktop and web world, I wanted to share this guide about mouse hover effects and animations. You will learn how to create a nice mouse hover effect in a Flutter app that will make a widget bigger on hover and return to its original size on exit.\nAs hovering requires a pointing device like a mouse, it will only work on platforms that support this kind of input behaviour. It’s not suited for mobile apps optimized for touch gestures!\nHere is the full example code:\nFirst, we add the SingleTickerProviderStateMixin to our state class. The SingleTickerProviderStateMixin is a mixin in Flutter that provides a single Ticker. A Ticker is essentially a timer that “ticks” every frame, allowing you to run animations or other periodic tasks in your Flutter app. If you have multiple animations running at the same time, use TickerProviderStateMixin instead.\nclass _State extends State\u0026lt;HoverTest\u0026gt; with SingleTickerProviderStateMixin initState In the initState method, we initialize the AnimationController and the Animation. The controller needs a Duration and the vsync property that we can simply assign this since we include the mixin I previously mentioned. The animation is a Tween of type double, has a begin and an end property, and needs the animation controller passed as an argument when animate() is called.\nlate AnimationController controller; late Animation\u0026lt;double\u0026gt; myAnimation; @override void initState() { super.initState(); controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 300)); myAnimation = Tween\u0026lt;double\u0026gt;(begin: 72.0, end: 96.0).animate(controller); } dispose The dispose method takes care of cleaning up the animation controller. Don’t forget this or your app will have memory leaks.\n@override void dispose() { controller.dispose(); super.dispose(); } In the build method, we have an AnimatedBuilder that builds the widget to animate. It’s an Icon widget in our case. Since we want to animate the size of the icon, we set the size property to the current animation value. We declared the animation property as Tween\u0026lt;double\u0026gt; because the size property of the Icon widget is of type double.\nbuild method To start the animation on mouse hover, we wrap the widget in a MouseRegion and handle the onEnter and onExit methods. When the cursor enters the widget, we want it to animate from the small size (our begin value) to the big size (our end value) and when the cursor leaves the widget, the opposite should happen. If you want, you can also change the cursor with the cursor property of the MouseRegion widget.\n@override Widget build(BuildContext context) { return Center( child: SizedBox( width: 100, height: 100, child: MouseRegion( cursor: SystemMouseCursors.click, onEnter: (_) =\u0026gt; _hover(true), onExit: (_) =\u0026gt; _hover(false), child: AnimatedBuilder( animation: myAnimation, builder: (context, child) =\u0026gt; Icon(Icons.photo, size: myAnimation.value))))); } void _hover(bool isHovered) { if (isHovered) { controller.forward(); } else { controller.reverse(); } } And here is the result:\nHover animation in a Flutter app\nConclusion In this article, you learned how to create a nice mouse hover effect in a Flutter app that makes a widget bigger on hover and smaller on exit. A few lines of code is everything you need to achieve this amazing result.\n","permalink":"https://quickcoder.org/posts/how-to-create-a-nice-mouse-hover-effect-in-a-flutter-app/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eLearn how to create a nice mouse hover effect in a Flutter app to make it more appealing on the desktop and web platform.\u003c/p\u003e\n\u003cp\u003eSince Flutter is gaining more and more traction in the desktop and web world, I wanted to share this guide about mouse hover effects and animations. You will learn how to create a nice mouse hover effect in a Flutter app that will make a widget bigger on hover and return to its original size on exit.\u003c/p\u003e","title":"How To Create A Nice Mouse Hover Effect In A Flutter App"},{"content":"Introduction We cover a basic example of how to write unit tests for a Flutter application. Explained code samples are included. Here is how to write unit tests for a Flutter application!\nTesting is an essential task in every software project (or at least it should be one). This time we are focussing on how to write unit tests for a Flutter application.\nI assume you are familiar with the concept of unit testing, the why and the what for. So we will focus on writing the test cases and how to structure them. This is mainly for people who didn’t write many or any tests for a Flutter application at all. It will give you a compact overview to start with. A link to the used sources is provided at the end.\nThis is the class we are going to write unit tests for.\nclass CounterService { int _counter = 0; int get counter =\u0026gt; _counter; final int? maxCounterValue; CounterService({this.maxCounterValue}); /// Increases the counter value if [maxCounterValue] has not been reached yet. CounterService inc() { if (maxCounterValue == null || _counter \u0026lt; maxCounterValue!) _counter++; return this; } /// Decreases the counter value if [counter] is bigger than zero. CounterService dec() { if (_counter \u0026gt; 0) _counter--; return this; } } This class manipulates a counter value when the methods are called. There are some conditions to be considered and a maximum value that the counter can’t exceed.\nSetup Let’s assume your class to test is called counter_service.dart and sits under the lib folder in your project. Your matching test class will be called counter_service_test.dart and will be in the test folder structure. If you follow this convention the Flutter test runner can find and execute your tests. It can be triggered by the command flutter test while you are in the root folder of your project.\nTesting the inc() method First, we test the inc() method. What we want to achieve is that every code branch is executed. This means we can cover this by writing three tests.\nValidate that the counter is increased if there is no maxCounterValue set. Validate that the counter is increased if maxCounterValue is not reached yet. Validate that the counter is not increased if maxCounterValue is reached. test(\u0026#34;Verify counter can be increased\u0026#34;, () { final _sut = CounterService(); _sut.inc().inc().inc(); expect(_sut.counter, equals(3)); }); test(\u0026#34;Verify counter can be increased if below max value\u0026#34;, () { final _sut = CounterService(maxCounterValue: 2); _sut.inc().inc(); expect(_sut.counter, equals(2)); }); test(\u0026#34;Verify counter cannot be increased above max value\u0026#34;, () { final _sut = CounterService(maxCounterValue: 0); _sut.inc(); expect(_sut.counter, equals(0)); }); Every test creates an instance of the class to test (System Under Test, that’s why I call the variable _sut), manipulates data, and checks the results. Verification of results is done by the expect function, which takes the actual value and a Matcher to evaluate the outcome. Here are some examples of how to work with matchers.\ntest(\u0026#34;Some matcher examples\u0026#34;, () { // type check expect(CounterService(), isA\u0026lt;CounterService\u0026gt;()); // null check expect(null, isNull); expect(\u0026#34;null\u0026#34;, isNotNull); // bool check expect(3 == 3, isTrue); expect(3 != 3, isFalse); // equality check expect(\u0026#34;1\u0026#34;, equals(\u0026#34;1\u0026#34;)); // sign check expect(-1, isNegative); expect(1, isPositive); expect(0, isNonNegative); expect(0, isNonPositive); // list checks expect([], isEmpty); expect([2], isNotEmpty); }); Testing the dec() method This method is a bit simpler than the one before, we just need 2 tests to cover all possible outcomes.\nValidate that the counter can be decreased Validate that the counter cannot be decreased below zero test(\u0026#34;Verify counter can be decreased\u0026#34;, () { final _sut = CounterService(); _sut.inc().inc().inc(); expect(_sut.counter, equals(3)); _sut.dec(); expect(_sut.counter, equals(2)); }); test(\u0026#34;Verify counter cannot be decreased below zero\u0026#34;, () { final _sut = CounterService(); _sut.dec(); expect(_sut.counter, equals(0)); }); As you can see every test follows the Arrange-Act-Assert pattern. It helps to structure the tests and is one of the most common strategies to write test code.\nLine 2–4 is the “Arrange” part where data is set up. Line 6 is the “Act” part where code is executed that we want to test. Line 7 is the “Assert” part where we check if the executed code did what it was expected to do. To execute the tests run the command flutter test. The test results will be printed to the console window. Many IDEs or editors support running tests as well.\nAdditional hints Use good test descriptions\nIf your tests increase in numbers you will most likely not remember what each test does. With a good description, you know more about the context and spend less time finding the problem.\nKeep your tests short\nOne should always see what a test does at first glance. If scrolling is necessary or many method calls are involved, it gets messy.\nMake your test independent of other tests\nA test should always work in the same way. So make sure to set up data properly (and even reset them after the test if needed).\nWrite tests while writing code\nDon’t skip the tests when you start. It is pretty certain that once you start writing tests you won’t cover everything as you would have if you had started writing them from the beginning.\nSource code You can find the source code on GitHub.\nConclusion In this article, you learned how to write unit tests for a Flutter application.\n","permalink":"https://quickcoder.org/posts/flutter-testing/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eWe cover a basic example of how to write unit tests for a Flutter application. Explained code samples are included. Here is how to write unit tests for a Flutter application!\u003c/p\u003e\n\u003cp\u003eTesting is an essential task in every software project (or at least it should be one). This time we are focussing on how to write unit tests for a Flutter application.\u003c/p\u003e\n\u003cp\u003eI assume you are familiar with the concept of unit testing, the why and the what for. So we will focus on writing the test cases and how to structure them. This is mainly for people who didn’t write many or any tests for a Flutter application at all. It will give you a compact overview to start with. A link to the used sources is provided at the end.\u003c/p\u003e","title":"How To Write Unit Tests For A Flutter Application"},{"content":"Introduction A ready-to-use and working build pipeline for Azure DevOps to build Flutter apps for iOS, Android, and Windows. All packages are store-ready!\nHere is a working pipeline for Azure DevOps to build Flutter apps with the most recent SDK version for iOS, Android, and Windows alongside an integration and configuration guide.\nIt will produce an Android App bundle (.aab), an iOS IPA file, and a Windows MSIX package which can be submitted to the corresponding app stores.\nThe pipeline uses the cloud agents of Microsoft and can benefit from parallel builds if available (paid option). It will execute the following steps for every platform:\nInstall Flutter SDK\nThe current stable channel version will be installed by default.\nBuild app (iOS, Android, Windows)\nEvery app is built in its own stage, so parallelism is supported.\nRun tests\nIf there are any *test.dart files in the project, they will be run here.\nPublish app as build artifact\nBuild artifacts can be downloaded or used in other pipelines\nHow can I create a pipeline from the YAML file? Your YAML file is checked into version control, how do we get it into Azure DevOps? It’s quite easy when you know where you have to go. Here are the steps:\nIn Azure DevOps go to Pipelines.\nClick the New pipeline button in the top right corner.\nAdd a new pipeline in Azure DevOps\nSelect your version control provider and the project that contains the YAML file. If you code is hosted in Azure DevOps select Azure Repos Git. Select code provider in Azure DevOps\nSelect Existing Azure Pipelines YAML file at the bottom Setup screen with predefined templates for new pipelines in Azure DevOps\nSelect the YAML file from the drop down. YAML file selection for a build pipeline in Azure DevOps\nThe pipeline will be created after the file is validated.\nWhat needs to be configured in the pipeline? First, you need to install the free Flutter Pipeline Tasks of Hey24Sheep in your organization so that the pipeline can use them. The package contains several tasks like, for example, downloading the SDK or building the application. The pipeline requires these tasks to be installed.\nThere are also some things you need to upload to Azure DevOps to make the pipeline work.\nAndroid Signing Key To obtain an Android signing key follow the official documentation of Flutter. You need two files: a .properties and a .jks file.\nApple Certificate Have a look at the official documentation on how to sign iOS apps. You need an iOS key file.\nApple Provisioning Profile A quick guide to create a provisioning profile can be found here if you don’t have it yet. You need a .mobileprovision file.\nThese files need to be uploaded into the Secure files part of the pipeline library. So go to your Azure DevOps instance, select Pipelines, then Library, and lastly the tab Secure files. Here you can add configuration files that should not be readable by anyone except the build pipeline. Choose descriptive names as the pipeline will reference them by name. If you match the names in the image below, the configuration will get a bit simpler as you won’t have to rename the existing file names.\nSecure file storage in Azure DevOps\nConfigure MSIX package information To customize the properties of the MSIX installer package, you need to create a section called msix_config in your pubspec.yaml file. Then you can define properties like the app name, version, and supported languages. Have a look at the documentation of the MSIX package to see what’s possible.\nThere are multiple ways to handle package signing. Here are some guidelines and helpful resources.\nSkip installer signing Without signing, the user will get a warning when running the package installer with your app. An installation can also be prevented depending on the system settings. Click HERE for an example of how the configuration can look in this case.\nSelf-signed certificate For private deployment and testing with the help of the MSIX installer, you need to give your application a digital signature in the form of a .pfx certificate. Click HERE for more details about how to create a certificate. And HERE is an example of how the msix_config section in your pubspec.yaml can look like.\nWindows Store handles signing When publishing to the Windows Store, you don’t need to take care of signing your package. The store handles everything for you. Click HERE for an example of how your config can look like.\nReferencing the secure files in the build pipeline The files are uploaded, but they need to be referenced from the pipeline. If you used the same file names as in the template, then it won’t be much work.\nSearch for the DownloadSecureFile tasks in the AndroidStage. Change the names of key.jks and key.properties as you need them. Then head to the following Bash task which will copy the downloaded files to the correct location. Adjust the paths and file names to your needs. If the signing fails it is most likely because the files are not in the correct location(s).\nIn the iOSStage look for the InstallAppleCertificate task. Update certSecureFile and certPwd to your needs. Then go to the InstallAppleProvisioningProfile task and update the provProfileSecureFile property with the correct file name. At last, check the FlutterBuild task and make sure that the exportOptionsPList links to the correct file in your folder structure.\nThe Setup is completed, you can run the pipeline now. There should at most be a permission warning message (to handle that see below).\nGranting permission for secure files on the first run After your pipeline is set up and you run it for the first time you will probably notice that there is a message.\nWarning hints for missing permissions of a build pipeline in Azure DevOps\nDialog for missing permissions before a pipeline can run in Azure DevOps\nYou need to authorize access to the secure files once and the pipeline will remember it until you change something.\nYou should consider an average runtime of about 10 minutes per platform. iOS sometimes tends to be slower but overall build speed is not the fastest, I must admit.\nAfter the process has been completed you will see a screen like below. The blue lines indicate where the created apps can be found and downloaded.\nBuild summary including build details, test results, and published artifacts on Azure DevOps\nSource code You can find the source code on GitHub.\nConclusion In this article, you learned how to build Flutter Apps for iOS, Android, and Windows with Azure DevOps.\n","permalink":"https://quickcoder.org/posts/flutter_build_pipeline/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eA ready-to-use and working build pipeline for Azure DevOps to build Flutter apps for iOS, Android, and Windows. All packages are store-ready!\u003c/p\u003e\n\u003cp\u003eHere is a working pipeline for Azure DevOps to build Flutter apps with the most recent SDK version for iOS, Android, and Windows alongside an integration and configuration guide.\u003c/p\u003e\n\u003cscript src=\"https://gist.github.com/xeladu/bf9318a65a097165ccc234a48b733a38.js\"\u003e\u003c/script\u003e\n\u003cp\u003eIt will produce an Android App bundle (.aab), an iOS IPA file, and a Windows MSIX package which can be submitted to the corresponding app stores.\u003c/p\u003e","title":"Build Flutter Apps For iOS, Android, And Windows With Azure DevOps"},{"content":"Introduction A productive app should not have it. So here is how you can remove the Flutter debug banner in your applications.\nThe debug banner should indicate that an app is currently under development. By default, it is displayed when you launch your Flutter app. A lot of beginners struggle with it and need help. However, the change is quite simple. So here is how to remove the debug banner in a Flutter application.\nIn case you don\u0026rsquo;t know what we are talking about, here is a screenshot of the said debug banner in a Flutter application.\nFlutter app with a visible debug banner in the upper right corner\nIf you don’t want the banner to be visible, you just need to set the debugShowCheckedModeBanner property of your MaterialApp widget to false.\nHere is the code:\n@override Widget build(BuildContext context) { return MaterialApp( title: \u0026#39;Flutter Demo\u0026#39;, debugShowCheckedModeBanner: false, theme: ThemeData(primarySwatch: Colors.blue), home: const Home2()); } Flutter app without a debug banner\nAnd then, the label is gone! Easy as that!\n🔔 Hint\nWhen your app is built in release mode, the banner is automatically disabled.\nConclusion In this guide you learned how to remove the debug banner in a Flutter application that is shown by default. One property change is enough, so it\u0026rsquo;s a quick solution. For more basic guide, have a look at the related articles.\n","permalink":"https://quickcoder.org/posts/remove-flutter-debug-banner/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eA productive app should not have it. So here is how you can remove the Flutter debug banner in your applications.\u003c/p\u003e\n\u003cp\u003eThe debug banner should indicate that an app is currently under development. By default, it is displayed when you launch your Flutter app. A lot of beginners struggle with it and need help. However, the change is quite simple. So here is how to remove the debug banner in a Flutter application.\u003c/p\u003e","title":"How To Remove The Debug Banner In A Flutter Application"},{"content":"Introduction In this article, I’ll show you how to store Flutter app data on your device with the plugin shared_preferences. It\u0026rsquo;s the default solution when you want to store some data!\nShared Preferences is a useful tool for persisting small amounts of data, such as user settings. It\u0026rsquo;s a key-value store similar to a Dictionary in Dart. The API is easy to learn, with getter and setter methods for various types like Int, Double, Bool, String, and StringList. Let’s learn how to store Flutter app data on your device with it!\nIt’s available for all major platforms like Android, iOS, macOS, Windows, Linux, and web. Under the hood, it uses the native SDKs provided by the operating system.\nReading and writing primitive data Shared Preferences stores the data on the local hard drive. When you relaunch your app, you can load the data from disk and continue working as before.\nHere is a code example showing how to read and write data with the Shared Preferences package:\nWorking with complex objects To store complex data objects, we use the following approach:\nImplement your custom class Add a fromJson factory constructor that constructs an object from a Map\u0026lt;String, dynamic\u0026gt; Add to toJson method that returns a Map\u0026lt;String, dynamic\u0026gt; from your object Save it with await prefs.setString(\u0026quot;key\u0026quot;, jsonEncode(myObject.toJson())) Load it with CustomClass.fromJson(jsonDecode(prefs.getString(”key”))) Here is a short example of such a class:\nWith this approach, you can store any complex object with the Shared Preferences plugin.\nTesting When you have tests that rely on Shared Preferences, you can use a mock implementation. The following code shows how to set values for testing:\nfinal Map\u0026lt;String, Object\u0026gt; values = \u0026lt;String, Object\u0026gt;{\u0026#39;counter\u0026#39;: 1}; SharedPreferences.setMockInitialValues(values); In this test mode, Shared Preferences will not store any data on persistent storage. All operations only work in memory.\nConclusion In this article, you learned how to store Flutter app data on your device with the plugin shared_preferences. It works well with primitive and complex data types and has great testing support.\n","permalink":"https://quickcoder.org/posts/how-to-store-flutter-app-data-on-your-device/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eIn this article, I’ll show you how to store Flutter app data on your device with the plugin shared_preferences. It\u0026rsquo;s the default solution when you want to store some data!\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://pub.dev/packages/shared_preferences\"\u003eShared Preferences\u003c/a\u003e is a useful tool for persisting small amounts of data, such as user settings. It\u0026rsquo;s a key-value store similar to a \u003ccode\u003eDictionary\u003c/code\u003e in Dart. The API is easy to learn, with getter and setter methods for various types like \u003ccode\u003eInt\u003c/code\u003e, \u003ccode\u003eDouble\u003c/code\u003e, \u003ccode\u003eBool\u003c/code\u003e, \u003ccode\u003eString\u003c/code\u003e, and \u003ccode\u003eStringList\u003c/code\u003e. Let’s learn how to store Flutter app data on your device with it!\u003c/p\u003e","title":"How To Store Flutter App Data On Your Device"},{"content":"Introduction Firebase is the perfect choice to add cloud capabilities to your apps. Here is how to create a Firebase project and link it with your Flutter app. Start your Firebase journey with this guide!\nFirebase is a lightweight app development platform of Google offering various services like databases, authentication, messaging, and many more. It offers SDKs for different programming languages and is well-documented. In this article, you\u0026rsquo;ll learn how to create a Firebase project and link it with your Flutter app.\nThis article covers the following sub-topics:\nCreate a new Firebase account Create a new Firebase project Create a new Firebase app Upgrade your billing plan Check your usage data Manage users and permissions Create a new budget Create a new Firebase account Setting up an account is pretty easy. Just go to console.firebase.google.com, click on Get Started, and log in with your existing Google account. Otherwise, you need to create a new one.\nFirebase welcome screen by author\nAnd that’s it! Now, you need to create your first project.\nCreate a new project Creating a project is done within a minute. Just follow these steps and have a look at the explaining images.\n1. Click on Add project in your Firebase dashboard on console.firebase.google.com\nScreenshot while adding a new firebase project by author\n2. Choose a name for your project\nScreenshot while choosing a Firebase project name by author\n3. You will be asked to enable Google Analytics but you can also disable it if you want\nThe final screen of the Firebase project creation process by author\n4. A click on Create project finishes the process.\nYour new project will appear in the dashboard and is ready for use!\nConnect your Flutter app with Firebase To access Firebase from your Flutter app, you need to do some setup steps.\n1. Install Flutter SDK and create a Flutter project\n2. Install Firebase CLI tools\n3. Log into your Firebase account with the firebase login command\n4. Install the FlutterFire CLI with any command-line tool from any location dart pub global activate flutterfire_cli\n5. Navigate to your Flutter project root folder and run the command flutterfire configure --project=\u0026lt;project-id\u0026gt; or just flutterfire configure\nA file firebase_options.dart will be created in your lib folder. However, it will produce build errors. This file contains all the required information to work with Firebase. If you change your Firebase configuration (by adding a web app for example), you need to run the configuration command again.\n6. Add the Firebase core plugin flutter pub add firebase_core The build errors should have been fixed.\n7. Initialize Firebase from your main method.\nFuture main() async { await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); runApp(App()); } Can’t find your project id? Look in the project settings ?\nFirebase project settings screenshot by author\nOr use the command firebase projects:list in your command-line tool.\nYou can also use a wizard in Firebase that provides the same setup instructions. Just click on the Flutter icon when you are on your Firebase project overview dashboard.\nAdd Flutter app to Firebase screenshot by author\nThat’s the whole magic. You can now start developing your app with available Firebase plugins ⭐\nUpgrade your billing plan After learning how to create a Firebase project and link it with your Flutter app, we can now look at billing. By default, your account uses the Spark plan which has free usage quotas for some of the Firebase features. To enable the full potential, you need to switch to the Blaze plan. With that plan, you will be charged for usage after the free limit is reached. There are no regular base fees to pay. To compare the details of the plans, see this link.\n💡 Tip\nIf you don’t host any web services, occupy any database storage, or use any functions of Firebase, you won’t be charged anything. There is no monthly fee.\n❗Warning\nYou will need to set up a billing account that requires a credit card when changing to the Blaze plan.\nFollow these steps to switch your plan:\nOn your Firebase project dashboard, click on Upgrade in the lower-left corner Upgrade link screenshot by author\nClick on Select Plan in the Blaze column Plan selection screenshot by author\nSet an optional billing alert if you want and continue Billing alert setup screenshot by author\nFinally, click on Purchase to complete the process Purchase confirmation screenshot by author\nYour plan is upgraded now. You can use additional Firebase features like Machine Learning, Test Lab, or Functions.\nCheck your usage data If you are going to build an app with lots of users, you need to get familiar with the usage and billing dashboard. As the Google services scale up when the load is high, your costs could be unexpectedly high. The Firebase usage and billing dashboard calculates costs per day and is a good way to keep an eye on the money.\nOn your Firebase project dashboard, click on the gears and select Usage and billing from the list Firebase usage and billing link screenshot by author\nYou will see an overview of your running costs that will be charged at the beginning of the next month Firebase usage and billing overview screenshot by author\nIt is that simple to keep track of your current costs with Firebase ?\nManage users and permissions To manage users and permissions, Firebase offers another dashboard in the project settings to add or remove members and assign roles to them.\nOn your Firebase project dashboard, click on the gears and select Users and permissions from the list Show Firebase users and permissions link screenshot by author\nUse the dashboard to manage members and roles. For advanced settings, there is a link to the Google Cloud Console in the lower-right corner. List Firebase users and permissions dashboard screenshot by author\nTo invite a new member, click the Add member button, and insert the mail address and the desired role. Firebase makes user management pretty easy in my opinion.\nCreate a new budget alert Budget alerts are great when you don’t want to exceed a certain amount of money every month, but you also don’t want to track your costs every day. A budget alert will notify you in case an event happens (for example 90% of your budget is reached). That way you don’t have to worry about costs.\nTo set up one or more budget alerts, go to the usage and billing dashboard (see section Check your usage data) and switch to the tab Details \u0026amp; settings.\nFirebase details and settings dashboard screenshot by author\nUse the button Create first budget (or View budgets if you already have at least one) to manage your budgets. You’ll be redirected to the Google Cloud Console, but the wizard is easy to understand.\nConclusion In this article, I showed you how to create a Firebase project and link it with your Flutter app. You can now use all the powerful Firebase services to build your apps!\n","permalink":"https://quickcoder.org/posts/firebase-flutter-setup/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eFirebase is the perfect choice to add cloud capabilities to your apps. Here is how to create a Firebase project and link it with your Flutter app. Start your Firebase journey with this guide!\u003c/p\u003e\n\u003cp\u003eFirebase is a lightweight app development platform of Google offering various services like databases, authentication, messaging, and many more. It offers SDKs for different programming languages and is well-documented. In this article, you\u0026rsquo;ll learn how to create a Firebase project and link it with your Flutter app.\u003c/p\u003e","title":"How To Create A Firebase Project And Link It With Your Flutter App"},{"content":"Introduction This short article shows how to install packages in your Flutter app so that you can use existing code and you don’t have to write everything yourself.\nWith packages, the development of apps can be accelerated and simplified by a lot. We have a look at how to find packages, how to add them to our app, and how we can use them in the code. For demonstration purposes, I am going to use the package url_launcher in this example. Here is how to install packages in your Flutter app.\nFind packages Most Flutter and Dart packages are hosted in the official package repository on pub.dev. You can use it to get detailed information about a package, a usage example, and installing guidelines.\nA collection of famous and good packages is Flutter Awesome. If you need inspiration on what packages are out there, check out this website.\n✅ Did you find a package that you want to use? Then let’s install it together in the next part.\nPackages on pub.dev There are two ways to install packages in your app. Either by using the command-line interface or by manually adding entries to your pubspec.yaml file.\nInstall with Flutter CLI Navigate to the root folder of your project and execute the command flutter pub add url_launcher. This will add a new entry in your pubspec.yaml file with the most recent version of the package available. Furthermore, it will download the sources and make the package ready for use.\nInstall manually Add a new entry in your pubspec.yaml file under the dependencies node with the name of the package and a version specifier like this: url_launcher: ^6.0.20. You can find the most recent version on the package’s page on pub.dev.\nPackage url_launcher in pubspec.yaml\nAfterward, you need to execute the command flutter pub get to make the package ready for use. Some IDEs like Visual Studio Code will automatically execute the command when the pubspec.yaml file has changed.\n✅ Is your package installed? Great! Then let’s use it in the code!\nUse packages To use a package in your code, you need to import it. In our example the import command looks like this:\nimport \u0026#39;package:url_launcher/url_launcher.dart\u0026#39;; For usage instructions, check the example tab on pub.dev for your package.\nPackages on your local machine There are cases when you need to reference a package from your local file system. With the following example, you can include it in your pubspec.yaml:\ndependencies: library_name: path: /path/to/library_name The path needs to point to the root folder of the library!\nPackages in a GIT repository Let\u0026rsquo;s assume, your package is not hosted on pub.dev but in a GIT repository like GitHub, Bitbucket, or others. Then, you can reference it in your pubspec.yaml with the following lines:\ndependencies: library_name: git: url: https://github.com/user/library_name.git ref: main This will reference the main branch of the repository. You can also use other branch names, commit ids, or tags.\nIf the repository is private and you have an access token, use the following schema:\ndependencies: library_name: git: url: https://\u0026lt;access-token\u0026gt;@github.com/user/library_name.git ref: main SSH access to private repositories is also possible with this approach:\ndependencies: library_name: git: url: git@github.com:user/library_name.git Packages in a custom repository If your package lies in a custom repository, you can also include it in your Flutter apps. Reference it in your pubspec.yaml like this:\ndependencies: library_name: hosted: \u0026lt;repo-url\u0026gt; version: ^1.11.9 Usually, you have a token to authenticate with your repository. Run the command dart pub token add \u0026lt;repo-url\u0026gt; and then enter the token in the next step. From now on, the tooling will use this token whenever it needs to access the given repository.\nA major benefit of custom repositories is that you can specify versions that you want to use just like with official package repository pub.dev.\nConclusion With this guide, you should know how to install packages in your Flutter app. For more information about packages go to the official Flutter documentation.\n","permalink":"https://quickcoder.org/posts/how-to-install-packages-in-your-flutter-app/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eThis short article shows how to install packages in your Flutter app so that you can use existing code and you don’t have to write everything yourself.\u003c/p\u003e\n\u003cp\u003eWith packages, the development of apps can be accelerated and simplified by a lot. We have a look at how to find packages, how to add them to our app, and how we can use them in the code. For demonstration purposes, I am going to use the package \u003ca href=\"https://pub.dev/packages/url_launcher\"\u003eurl_launcher\u003c/a\u003e in this example. Here is how to install packages in your Flutter app.\u003c/p\u003e","title":"How To Install Packages In Your Flutter App"},{"content":"Introduction People like charts. They are an easy way to display data. So here is how to create beautiful line charts in a Flutter app. This is perfect for any dashboard you want to build!\nWith line charts, you can visualize a variety of data. Think about daily sales of the last 30 days or accumulated revenues of the current year. It’s a perfect way to show users values that change over time. In this article, I want to show you how to create beautiful line charts in a Flutter app.\nEventually, this is what you can build:\nA line chart created with the package fl_chart for Flutter\nInstall the package I use the package fl_chart.\nWhy? Because it’s the best I found so far.\nIt is actively developed (GitHub), supports multiple chart types, and has outstanding documentation with code examples. I can recommend it without exceptions.\nNo idea how to install a package in your Flutter app? Check out this article!\nProvide chart data Let’s start with the basic LineChart widget.\nThe data points are passed in with the spots property of the LineChartBarData object. A FlSpot object is just a 2-dimensional coordinate in a grid with an x and a y value. The chart uses those points to draw a line with the given settings of the LineChartBarData object. You can also pass multiple LineChartBarData objects which will result in multiple individual lines.\nClick here for a description of all properties that you can use.\nGenerate axis descriptions In my example, the bottom and the right axes contain custom descriptions. The bottom axis shows dates while the right axis shows a variety of numbers.\nThe chart can also auto-generate axes descriptions but it doesn’t work that well in my opinion. So here is my approach.\nIn general, you tell the widget how many axis descriptions you want to have with the interval property, what you want to display with the getTitlesWidget function, and how much space you want to reserve for the axis with the reservedSpace property.\nBottom axis Here is the code for the bottom axis:\nMy chart uses one data point per month. This means, my spots property contains 19 objects with different x values. They look something like this\n{ 0: 5, 1: 15, 2: 30, 3: 50, 4: 60, …, 18: 540 } Since my x values always increase by 1, I simply set the interval property to 1 and the chart generates an axis description at every data point with the getTitlesWidget function. If you want more lines, reduce the value and increase it for fewer lines.\nThe getTitlesWidget will be called at every interval providing the current index as the first argument. The function then translates the index into a month and year combination.\nindex is 0 ⇒ take the current date, subtract 19 months, and format the output to MMM yy index is 1 ⇒ take the current date, subtract 18 months, and format the output to MMM yy … index is 19 ⇒ take the current date, subtract 0 months, and format the output to MMM yy I omitted the code for the translation but I think you get the idea of what’s possible. If you just want to display the x coordinate, a simple getTitlesWidget: (val, meta) ⇒ Text(val.toString()) is enough.\nRight axis My right axis displays sale numbers with exactly 5 axis descriptions which adapt to whatever values are provided. Here is the code:\nNotice the interval which is set to 0.25. This helps me to get exactly 5 axis descriptions. To achieve this, I set the maxY property of the LineChartData object to 1. Then, the chart will insert descriptions at the values of 0, 0.25, 0.5, 0.75, and 1. Of course, you can also use the original values and calculate with them. But the 0-1 interval feels easier to understand for me.\nSince my chart adapts dynamically to the provided values, I created a function that calculates the axis descriptions based on the maximum value of the provided FlSpot objects.\nWhen the max value is between 500 and 999, it will create description values of 0, 250, 500, 750, and 1000.\nWhen the max value is between 250 and 499, it will create description values of 0, 125, 250, 375, and 500.\nI think you get the idea ?\nBut for this to work, I needed to scale all y-values to the interval between 0 and 1. With the previous data object, the following y-values would be calculated:\n// divide all values by the max axis value (1000 in this case) { 0: 0.005, // (5/1000) 1: 0.015, // (15/1000) 2: 0.030, // (30/1000) 3: 0.050, // (50/1000) 4: 0.060, // (60/1000), …, 18: 0.540 // (540/1000) } Again, this is a rather complex use case to showcase the power of the widget.\nFind out more about customizing axis descriptions in the official documentation.\nDisable axis titles To disable axis titles, use the following code:\nCustomize tooltips Tooltips are also not that difficult to create. The LineTouchData object has a touchTooltipData property which contains a getTooltipItems function.\nThe function contains all spots of all chart lines at the current position. In my example, I just grab the current axis description of that spot alongside the value and place them underneath each other.\nA line chart widget with a tooltip\nRead more about customizing tooltips in the documentation here.\nDraw grid lines Drawing grid lines is pretty easy. You specify the horizontal and vertical intervals and handle the getDrawingVerticalLine and getDrawingHorizontalLine functions. By default, you get a dashed line like in the screenshots above but you can also customize the FlLine object to your liking.\nIn my example, I go with the default settings and just define the intervals so that the grid matches the axis descriptions.\nHere is more information about drawing grid lines in charts:\nExamples For more examples, check out these code snippets.\nConclusion In this article, I showed you how to create beautiful line charts in a Flutter app. With the powerful package fl_chart, it’s really easy to create custom data visualizations and enhance your apps.\n","permalink":"https://quickcoder.org/posts/how-to-create-beautiful-line-charts-in-a-flutter-app/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003ePeople like charts. They are an easy way to display data. So here is how to create beautiful line charts in a Flutter app. This is perfect for any dashboard you want to build!\u003c/p\u003e\n\u003cp\u003eWith line charts, you can visualize a variety of data. Think about daily sales of the last 30 days or accumulated revenues of the current year. It’s a perfect way to show users values that change over time. In this article, I want to show you how to create beautiful line charts in a Flutter app.\u003c/p\u003e","title":"How To Create Beautiful Line Charts In A Flutter App"},{"content":"Introduction Microsoft is a big player in the enterprise environment. So here is how to log in from a Flutter Web App with a Microsoft organization account. Once the setup is complete, it works flawlessly!\nYou have a Flutter Web App (no Android or iOS) and need to add the possibility that users can log in with their Microsoft organization accounts. It sounds like a rather simple task but it took me quite a while to solve it. To save you the trouble, here is how to log in from a Flutter Web App with a Microsoft account.\n💡 Tip\nI use Firebase Authentication because it’s the only way that works for me. I tried many Flutter packages (most of them use msal.js under the hood) but without success. If you have another approach, tell me in the comments!\nAzure Setup This section describes what you need to do in Microsoft Azure for the authentication to work. The following steps are involved:\nCreate an app registration Create a client secret Create a platform configuration with a redirect URL Grant access permissions App registration The first step is to create a new app registration. Go to entra.microsoft.com, sign in with your organization account, and jump to the menu entry App registrations.\nCreate a new app registration in Microsoft Entra Admin Center\nThen, create a new registration but skip the redirect URI for now.\nCreate a new app registration without a redirect URI\nIn the overview, you have your Application (client) ID and your Directory (tenant) ID. We need both values in the future.\nApplication (client) ID and Directory (tenant) ID of your new app registration\nClient secret Now, let’s create a client secret. Open the Certificates \u0026amp; secrets menu of your app.\nOpen the Certificates \u0026amp; secrets menu to create client secret\nClick on New client secret.\nClick the button to create a new client secret\nCreate your secret and confirm. Make sure to copy the value immediately because once you leave this page, you cannot copy it anymore. Store it in a text file until we need it again.\nCopy the client secret after creation and store it in a text file\nPlatform configuration 💡 Tip\nPlease continue with the Firebase setup in the next section. You can’t complete this step right now. Once Firebase is set up, you can come back here and finish the Azure configuration!\nThe final step is to create a platform configuration with a redirect URL that you can do in the Authentication menu.\nOpen the Authentication menu to create a platform configuration\nClick on Add a platform.\nClick the button to add a platform configuration\nChoose Web (it won’t work with a Single-page application!)\nClick on the web platform. Do not choose Singe-page application here!\nCopy the redirect URL from Firebase to Azure and confirm.\nInsert the redirect URL from Firebase and save the configuration\nAccess permissions (only if needed) If you get an authentication error indicating missing permissions, then here is how fix deal with it. Go to the API permissions menu.\nThe API permissions menu handles all user permissions\nClick on Grant admin consent for [org].\nClick on Grant admin consent for [org] to allow all users in your organization to authenticate\nTry to authenticate again and it should work now!\nFirebase Setup If you don’t have a Firebase account and project and also don’t know what I am talking about, please read this article first. After that, you have a better understanding of the following steps.\nIn your Firebase Console, select Authentication from the Build menu.\nFirebase Authentication menu entry\nSwitch to the tab Sign-in method, click on Add new provider, choose Microsoft, add the client id and client secret from your Azure configuration, copy the redirect URL, and confirm with a click on Save.\nPass client id and client secret to the authentication provider.\nThen, go back to your Azure app registration and continue with the step Platform configuration from the previous section.\nThe Firebase and Azure configurations are completed. Now, let’s write a small Flutter app and try it out!\nFlutter App Setup Install the following packages to work with Firebase Authentication:\nFirebase Core\nFirebase Authentication\n💡 Tip\nNot sure how? Here is a guide!\nThe next step is to connect your Flutter app with your Firebase project. Run the command flutterfire configure, choose your project, select the web platform, and wait for the process to finish. A file firebase_options.dart will be created in your lib folder.\n💡 Tip\nNeed more details with setting up Firebase? Check out this guide!\nAfter that, open your main.dart file in the lib folder and change the main() method to the following:\nimport \u0026#39;package:your_package/firebase_options.dart\u0026#39;; import \u0026#39;package:firebase_core/firebase_core.dart\u0026#39;; ... Future\u0026lt;void\u0026gt; main() async { await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); runApp(const MyApp()); } Now, you can initiate the authentication process with the following code block:\nimport \u0026#39;package:firebase_auth/firebase_auth.dart\u0026#39;; import \u0026#39;package:firebase_core/firebase_core.dart\u0026#39;; ... try { final provider = OAuthProvider(\u0026#34;microsoft.com\u0026#34;); provider.setCustomParameters( {\u0026#34;tenant\u0026#34;: \u0026#34;your-tenant-id\u0026#34;}); var cred = await FirebaseAuth.instance.signInWithPopup(provider); print(cred.user!.displayName!); print(cred.user!.email!); } on FirebaseAuthException catch (ex) { print(\u0026#34;${ex.code} - ${ex.message}\u0026#34;); } Replace your-tenant-id with the corresponding value from your Azure subscription.\nNow, you can run your code and test the authentication.\nPitfalls Here are some things that can go wrong during the setup. Make sure to avoid them.\nDon’t forget the tenant id! The tenant (directory id) needs to be passed to Firebase Authentication. Otherwise, it will use the \u0026lsquo;common\u0026rsquo; endpoint which does not work for organizations. The tenant id can be found in Azure in the \u0026ldquo;Overview\u0026rdquo; section of your Azure App.\nMissing permissions! If authentication fails because of missing admin privileges, go to entra.microsoft.com, switch to your organization and your app, go to the \u0026ldquo;Permissions\u0026rdquo; section and click on the button \u0026ldquo;Grant admin consent for [org]\u0026rdquo;. Authentication should then work fine.\nUse Web App, not SPA! The Azure App must be configured as a Web app, not a SPA (Single Page Application) app. Otherwise, authentication will fail.\nUse Entra Admin Center! Don\u0026rsquo;t use the Azure Portal to create app registrations, use the Microsoft Entra Admin Center at https://entra.microsoft.com.\nConclusion In this article, you learned how to log in from a Flutter Web App with a Microsoft account.\n","permalink":"https://quickcoder.org/posts/how-to-log-in-from-a-flutter-web-app-with-a-microsoft-account/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eMicrosoft is a big player in the enterprise environment. So here is how to log in from a Flutter Web App with a Microsoft organization account. Once the setup is complete, it works flawlessly!\u003c/p\u003e\n\u003cp\u003eYou have a Flutter Web App (no Android or iOS) and need to add the possibility that users can log in with their Microsoft organization accounts. It sounds like a rather simple task but it took me quite a while to solve it. To save you the trouble, here is how to log in from a Flutter Web App with a Microsoft account.\u003c/p\u003e","title":"How To Log In From A Flutter Web App With A Microsoft Account"},{"content":"Introduction Firebase Hosting offers a neat feature to automatically deploy a web app to a preview channel when a new pull request is created on GitHub. With that feature, testers can verify changes and give constructive feedback while the productive web app is not affected. But without proper configuration, the GitHub Action can run into errors. So here is how to fix Firebase Hosting errors when using automatic deployment with GitHub.\nLet’s assume, your GitHub setup is already in place. If not, then here is a short description of how you can set it up. Your workflow looks like this:\nCreate a new branch Make some changes Commit and push the changes Create a pull request on GitHub The GitHub Action is triggered and executes the workflow. In my example, I am using a Flutter web app. Before the deployment, I installed the GitHub Action for Flutter to build the web app. In the next step, the app gets deployed to a preview channel.\n💡Tip\nA preview channel in Firebase Hosting is a temporary channel that usually lives for a week by default and is then automatically deleted. It has its own URL and is independent of the live channel.\nIf you are running into a 403 error (see image below) during this step, your configuration of your GitHub project is wrong. But don’t worry, the fix is easy!\nGitHub Action error while deploying to Firebase Hosting. The error status code 403 indicates that the GitHub Action does not have enough permissions to execute the deployment.\nIn your GitHub project, click on Settings → Actions → General. Scroll down to Workflow permissions, enable the option “Read and write permissions”, and activate the check box “Allow GitHub Actions to create and approve pull requests”.\nScreenshot of the GitHub repository settings. Activate the radio button “Read and write permission” and enable the check box “Allow GitHub Actions to create and approve pull requests” to fix the errors above.\nRerun your pipeline and the errors won’t appear again. You should see an additional comment in your pull request with all details about the preview channel. The preview channel lives for 7 days before it automatically gets deleted. This should give you enough time to test the changes thoroughly.\nSuccessful preview channel deployment of the GitHub Action to Firebase Hosting. Details about the channel are added as a comment in the pull request.\nConclusion In this article, you learned how to fix Firebase Hosting errors when using automatic deployment with GitHub. The solution is pretty simple but you have to know what you need to do. This shouldn’t be a problem for you in the future anymore.\n","permalink":"https://quickcoder.org/posts/how-to-fix-firebase-hosting-errors-on-github/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eFirebase Hosting offers a neat feature to automatically deploy a web app to a preview channel when a new pull request is created on GitHub. With that feature, testers can verify changes and give constructive feedback while the productive web app is not affected. But without proper configuration, the GitHub Action can run into errors. So here is how to fix Firebase Hosting errors when using automatic deployment with GitHub.\u003c/p\u003e","title":"How To Fix Firebase Hosting Errors When Using Automatic Deployment With GitHub"},{"content":"Introduction Learn how to create beautiful charts and graphs in your Flutter application to create dashboards or visualize statistic evaluations. With this article you can get started quickly!\nIn this article, I\u0026rsquo;ll show you how to create beautiful charts and graphs in your Flutter application. I\u0026rsquo;ll cover the tools and methods to create good-looking and interactive charts. Whether you\u0026rsquo;re making a finance app, a data dashboard, or just want to make your app look better, learning how to make charts in Flutter can be a big help.\nTo create charts, I use the package fl_charts. It’s easy to set up, good to work with, and has an outstanding documentation. The following chart types are currently supported:\nLineChart\nBarChart\nPieChart\nScatterChart\nRadarChart\nI will show you the PieChart and the LineChart. As they all work similar, you won’t have trouble implementing the other types yourself.\nPieChart Let’s start with an image of what the end result can look like:\nA pie chart with a legend\nAnd here is some code. I’ll explain it in the following.\nA pie chart is just a collection of sections with various settings. Every section is represented by a PieChartSectionData object. I customized title, position, size, value, color, radius, and border. So as you can easy, there are a lot of options to play around with.\nThe PieChart object has some neat configuration options\ncenterSpaceRadius to change the size of the circle in the middle sectionsSpace to create gaps between sections startDegreeOffset to indicate where the first segment is located Chart legends are not supported. You have to build them yourself. I just went with a Column, the section colors, and the titles. Simple and effective!\nIf you want to handle touches or hovers, you can use PieTouchData. There is a touchCallback function that returns the touched section of the chart. In my case, I decided to make the section a bit larger and draw a border around it when touched. Animations are already built-in, so you don’t have to deal with them!\nA pie chart with animated segments\nOf course, there is much more to explore, but you should get the point of how to create pie charts with this package. Check out the documentation and code examples for further details.\nLineChart Once again, let’s get some inspiration and look at an example:\nA line chart with axis descriptions\nAnd the corresponding code to this image:\nA line chart is a collection of connected two-dimensional coordinates with optional axis captions or grid lines.\nThe lineBarsData array contains all lines within the chart. You can draw as many as you want. The LineChartBarData class allows you to customize for example the line (isCurved, curveSmoothness, width), what happens under the line (belowBarData), or what data points the line uses (spots).\nAxis scales and captions are created on all sides by default. Of course, you can customize each axes individually by overwriting the getTitlesWidget function and setting the interval of the SideTitles object of an axes. Here is a good example of how this works.\nTo change the grid appearance, use the FlGridData class. And if you want some interaction, read about touch handling here.\nConclusion In this article, you learned how to create beautiful charts and graphs in your Flutter application. The package fl_charts make it as easy as possible while also being flexible and customizable.\n","permalink":"https://quickcoder.org/posts/how-to-create-charts-and-graphs-in-your-flutter-application/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eLearn how to create beautiful charts and graphs in your Flutter application to create dashboards or visualize statistic evaluations. With this article you can get started quickly!\u003c/p\u003e\n\u003cp\u003eIn this article, I\u0026rsquo;ll show you how to create beautiful charts and graphs in your Flutter application. I\u0026rsquo;ll cover the tools and methods to create good-looking and interactive charts. Whether you\u0026rsquo;re making a finance app, a data dashboard, or just want to make your app look better, learning how to make charts in Flutter can be a big help.\u003c/p\u003e","title":"How To Create Beautiful Charts And Graphs In Your Flutter Application"},{"content":"Introduction Here is how to use the Scaffold widget in a Flutter application to create a consistent app skeleton in no time. It\u0026rsquo;s the most common basic building block in Flutter apps and here is how it works!\nIn Flutter, a Scaffold is a fundamental building block used to create the basic structure and layout for an app\u0026rsquo;s user interface. It provides a pre-designed app bar, a body area, and other essential components, making it easier for developers to create consistent and standard app layouts. Think of it as a template for your app\u0026rsquo;s screen with that you can start development faster.\nHere is a detailed guide of how to use the Scaffold widget in a Flutter application!\nbody The body is the main display area of your app and is always required. Feel free to do here whatever you want!\nappBar The AppBar resides at the top of the application. It’s a fixed container to show various information such as the app name, menus, or quick actions.\nYou can specify leading, title, and action widgets.\nIf the leading widget is not specified, it will be automatically populated with either a drawer icon (see next section) or a navigation arrow. To disable this behavior, set automaticallyImplyLeading to false.\nThe title widget usually contains the name of the app in a Text widget.\nThe actions section can be used to add a list of IconButton widget like save or cancel buttons.\nHere is an example of an AppBar:\nAn example of a customized AppBar in a Scaffold widget of a Flutter application\ndrawer/endDrawer Drawers are menus that are invisible by default on mobiles. If you see a hamburger icon in the top left or top right corner, it\u0026rsquo;s usually a drawer menu underneath. A user can open and close them by clicking the icon. In addition, the widget supports swipe gestures to open and close the menu.\nDrawers usually offer navigation options to different pages of an app. Typical examples include settings, user profile, login, or about pages.\nHere is a code example for a Drawer or EndDrawer in a Scaffold:\nTo close a drawer programmatically, use Navigator.of(context).pop().\nAn example video of a drawers in a Scaffold widget of a Flutter application\n❗Warning\nAn EndDrawer is only visible in the app bar by default if you have not set any actions!\n❗Warning\nA Drawer is only visible in the app bar by default if you have not set a leading widget!\npersistentFooterButtons persistentFooterButtons accepts a list of any widgets and those widgets appear permanently at the bottom of the screen.. Although the name implies that only buttons are supported, you can put any Widget here. To change the alignment, use the property persistentFooterAlignment.\nHere is a code example of how to add widgets to the bottom area:\nAn example of persistent footer buttons in a Scaffold widget of a Flutter application\nfloatingActionButton The FloatingActionButton is a button that floats on top of the UI at the specified location. You need to specify the child and the onPressed property. Usually, the button contains only an icon for an action that users need regularly like creating new entries in a list.\nThere are also alternative named constructors extended, small, and large. The small and large options are identical to the default one but make the button smaller or larger. The extended constructor has the options label and icon and is used best when you want to display text and icon together.\nTo control the position of the button, use the floatingActionButtonLocation property. The default value is endFloat.\nHere are some code examples for different FloatingActionButtons:\nAn example of a FloatingActionButton in a Scaffold widget of a Flutter application\nbottomSheet The bottomSheet is used to display additional information when required. Think of it as a context menu or a dialog. You can use it in two different ways.\nWhen you assign a widget to the bottomSheet property of a Scaffold, it is permanent. You cannot close it and it will stay on the screen. To be honest, I haven’t seen anyone using the widget in that way until now.\nThe second option is to use the showModalBottomSheet function to display a bottom sheet on demand. You can close it with a simple Navigator.of(context).pop().\nLet’s say you have a navigation bar at the bottom and there are more options than the screen size allows. With a bottom sheet, you can display additional options when requested by the user.\nHere is a code example of a persistent BottomSheet:\nAn example of a BottomSheet in a Scaffold widget of a Flutter application\nbottomNavigationBar bottomNavigationBar is similar to persistentFooterButtons. However, the navigation bar requires a list of BottomNavigationBarItems as children with a label and an icon. There are fewer styling options available but when you want the bottom area to be used for navigation, the BottomNavigationBar is the way to go.\nWith the currentIndex property, you set the active child item.\nHere is a code example of a BottomNavigationBar:\nAn example of a BottomNavigationBar in a Scaffold widget of a Flutter application\n💡 Tip\nDon’t use bottomNavigationBar and persistentFooterButtons at the same time. Choose one and stick with. If you want navigation, choose the navigation bar. Use the footer buttons for everything else!\nConclusion In this article, you learned how to use the Scaffold widget in a Flutter application. We covered various options to customize it with an AppBar, a FloatingActionButton, a Drawer, or a BottomSheet. There are many possibilities to choose from that makes the Scaffold an ideal starting template for any Flutter app.\n","permalink":"https://quickcoder.org/posts/how-to-use-the-scaffold-widget-in-a-flutter-application/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere is how to use the \u003ccode\u003eScaffold\u003c/code\u003e widget in a Flutter application to create a consistent app skeleton in no time. It\u0026rsquo;s the most common basic building block in Flutter apps and here is how it works!\u003c/p\u003e\n\u003cp\u003eIn Flutter, a \u003ccode\u003eScaffold\u003c/code\u003e is a fundamental building block used to create the basic structure and layout for an app\u0026rsquo;s user interface. It provides a pre-designed app bar, a body area, and other essential components, making it easier for developers to create consistent and standard app layouts. Think of it as a template for your app\u0026rsquo;s screen with that you can start development faster.\u003c/p\u003e","title":"How To Use The Scaffold Widget In A Flutter Application"},{"content":"Introduction Here is how to implement drag and drop in a Flutter application and build awesome user interfaces for all input types. Give your users some gestures to play around!\nDrag and drop is a common UX pattern on mobile, tablets, and desktops. It\u0026rsquo;s easy and convenient, regardless of the input type (finger, stylus, mouse) you use. Here\u0026rsquo;s how to implement drag and drop in a Flutter application.\nDraggable We start with the Draggable widget. This is the widget the users can drag in your Flutter app. You need to provide a child and a feedback widget.\nThe child widget is always displayed while the feedback widget is displayed during a drag operation and it follows the pointer.\nWith the data property you can assign any data object to the Draggable widget. This will be relevant during the drop operation.\nYou can wrap anything in a Draggable widget to offer drag and drop functionality in no time.\nHere is a code example of a Draggable with a Text widget when dragged:\n🔔 Hint\nBe aware that your feedback widget has no style information. You need to pass it or wrap it in a Material widget.\nDragTarget A DragTarget widget uses a builder function to determine its visual representation. The candidates argument contains the Draggable widgets and their assigned data objects.\ncandidates is empty by default. But as soon as a Draggable enters the DragTarget widget, it contains the assigned data objects of the Draggable. You can display two different UI states: when the user hasn’t dragged anything onto a target and when he is about to drop it.\nThe onAccept function triggers when the user drops a Draggable over a DragTarget. The passed data argument contains the corresponding data object of the Draggable if any.\nHere is the code for a DragTarget that reacts on Draggables when they are dropped:\nAnd here is a short demo video of how it can look eventually:\nDemo video of how to implement drag and drop in a Flutter application by author\nConclusion In this article, you learned how to implement drag and drop in a Flutter application and build awesome user interfaces. It’s an easy solution but can be a big gain in terms of usability.\n","permalink":"https://quickcoder.org/posts/how-to-implement-drag-and-drop-in-a-flutter-application/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere is how to implement drag and drop in a Flutter application and build awesome user interfaces for all input types. Give your users some gestures to play around!\u003c/p\u003e\n\u003cp\u003eDrag and drop is a common UX pattern on mobile, tablets, and desktops. It\u0026rsquo;s easy and convenient, regardless of the input type (finger, stylus, mouse) you use. Here\u0026rsquo;s how to implement drag and drop in a Flutter application.\u003c/p\u003e\n\u003ch1 id=\"draggable\"\u003eDraggable\u003c/h1\u003e\n\u003cp\u003eWe start with the \u003ccode\u003eDraggable\u003c/code\u003e widget. This is the widget the users can drag in your Flutter app. You need to provide a child and a feedback widget.\u003c/p\u003e","title":"How To Implement Drag And Drop In A Flutter Application"},{"content":"Introduction I never really had trouble with lists in dart. But only until I used null values. Don\u0026rsquo;t fall for this trap like I did!\nI came across a strange error during development of a game app recently. The game required a 2-dimensional array or list to represent a table structure.\nTo do this, I chose the data type List\u0026lt;List\u0026lt;MyObject\u0026gt;\u0026gt;. At the beginning, I initialized the structure with null values. Here is the code\nvar gameTable = List.filled(_answerRowHeaders.length, List.filled(_answerColumnHeaders.length, null)); During testing, I quickly noticed that whenever a cell got assigned a value, the entire column got assigned the same value. How could that happen?\nI couldn’t spot the problem in my code although it was a small widget class with less than 300 lines. Eventually I had to ask ChatGPT for help. A major benefit for me is the ability to feed ChatGPT some code and ask it about specific problems. You will most likely get a helpful answers. And it usually works well for me.\nnull can be tricky After several question rounds, ChatGPT noticed that my initialization code was the problem. List.filled fills a list with the same object passed as the fill argument. In my case, this was the null object of the inner list. I didn’t know that null references could be shared. But to be honest, I also didn’t face this problem before so I never really thought about it.\nThe correct way for me to initialize lists would have been to use List.generate.\nvar gameTable = List.generate(_answerRowHeaders.length, (row) =\u0026gt; List.generate(_answerColumnHeaders.length, (col) =\u0026gt; null)); With this code, you get different null objects for every entry in your list. And this is what solved my problem.\nConclusion Beware of the differences between List.filled and List.generate even if you want to use null as a placeholder. This can save you a lot of trouble during debugging!\n","permalink":"https://quickcoder.org/posts/how-not-to-initialize-lists-in-dart/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eI never really had trouble with lists in dart. But only until I used \u003ccode\u003enull\u003c/code\u003e values. Don\u0026rsquo;t fall for this trap like I did!\u003c/p\u003e\n\u003cp\u003eI came across a strange error during development of a game app recently. The game required a 2-dimensional array or list to represent a table structure.\u003c/p\u003e\n\u003cp\u003eTo do this, I chose the data type \u003ccode\u003eList\u0026lt;List\u0026lt;MyObject\u0026gt;\u0026gt;\u003c/code\u003e. At the beginning, I initialized the structure with \u003ccode\u003enull\u003c/code\u003e values. Here is the code\u003c/p\u003e","title":"How NOT To Initialize Lists In Dart: Avoiding Object Sharing And Null Value Surprises"},{"content":"Introduction Challenging your users with questions is common. So here is how to make a simple quiz page widget for Flutter apps.\nThere are tons of quiz apps out there in the app stores. If you ever thought about making your own, then here is a tutorial on how to make a simple quiz page widget for Flutter apps.\nEventually, it will look like this:\nA simple quiz widget for Flutter apps\nA simple quiz widget for Flutter apps\nModels Let’s start with the data models first. We don’t need really much to get started. A Question object contains the text and four 4 answers and an Answer object contains also text as well as an indicator if it is the correct answer.\nTo collect results and track progress, I also created a QuizState class.\nUser interface Now, we have to build a UI for the user. Here is my proposal. Feel free to adapt it as you need it.\nLet me guide you through the code. It should be fairly easy to understand for the most part.\nWe start with the state variables:\n_questionIndex - to know what the current question is. The questions are held in a list and this variable points to the current position.\ncurrentQuestion - a shortcut so that I don’t need to write widget.questions[_questionIndex] all the time.\n_loading - an indicator if the user has pressed a button to show the answer.\n_buttonStyles - Four ButtonStyle objects in a list to change the visual representation of the answer buttons.\nThe build method contains the usual stuff with a little twist at the beginning. When the quiz is finished, a summary is displayed instead of another question.\nBut what happens when the user clicks on an answer?\nLook at the _handleAnswer method. First, we set the _loading variable to true. Then we check if the answer is correct or not and apply the corresponding button style to the button that the user clicked. A call to setState rebuilds the user interface.\nAfter rebuilding the UI, the question is added to the QuizState object. Then, the loading variable and the button style are reset, and the index is increased. With the next call to setState, the next question (or the summary) is displayed. You can change the time between questions by increasing or decreasing the duration of the Future.\nThat’s all the magic!\nConclusion In this article, you learned how to create a simple quiz widget for Flutter apps. With this template, you can customize the code to fit your requirements.\n","permalink":"https://quickcoder.org/posts/how-to-make-a-simple-quiz-page-widget-for-flutter-apps/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eChallenging your users with questions is common. So here is how to make a simple quiz page widget for Flutter apps.\u003c/p\u003e\n\u003cp\u003eThere are tons of quiz apps out there in the app stores. If you ever thought about making your own, then here is a tutorial on how to make a simple quiz page widget for Flutter apps.\u003c/p\u003e\n\u003cp\u003eEventually, it will look like this:\u003c/p\u003e\n\u003cfigure\u003e\n\u003cp\u003e\u003cimg alt=\"A simple quiz widget for Flutter apps\" loading=\"lazy\" src=\"/posts/how-to-make-a-simple-quiz-page-widget-for-flutter-apps/images/quiz_widget.gif\"\u003e\u003c/p\u003e","title":"How To Make A Simple Quiz Page Widget For Flutter Apps"},{"content":"Introduction Firebase offers several services for storing data, including Firestore, Storage, and Realtime Database. However, it can be difficult to keep track of which data belongs together across service borders. Use my approach on how to connect data between different Firebase services. It has worked great for me in the past!\nUnique indentifiers (UID) When using Firebase Authentication, you get a UID per user. We can use this UID to identify corresponding data sets that belong to this user across services.\nIt’s a dead-simple way of how to connect data between different Firebase services.\nFirebase Firestore (upper part) contains a collection with the same UID as a user in Firebase Authentication (lower part). We can identify user data by using a common UID across all Firebase services.\nWhen using Firestore, you can use the UID as a collection ID.\nFor example, in a TODO app, you may want to associate all TODOs with a specific user. To do this, you can either create a collection for each user and set the UID as the collection ID, or add a user property to each TODO that stores the UID.\nThe first approach requires fetching the entire collection, while the second method requires adding a filter to the query. This involves a bit more coding and may be slower for large collections.\nThe same principle applies to Cloud Storage. Make the UID part of the path to easily identify which items belong to which user.\nCreate UIDs Firebase Authentication is an excellent tool for generating unique user IDs. Each registered user is assigned a unique identifier that remains the same, even if the app is deleted and reinstalled because it is associated with the user\u0026rsquo;s credentials, rather than the app itself.\nFirebase Storage (upper part) contains a folder with the same UID as a user in Firebase Authentication (lower part). We can identify user data by using a common UID across all Firebase services.\nOther possibilities are to create a microservice that generates those ids for you. You write a cloud function that returns a UID, make it accessible via a GET request, and deploy. Voilà, a super simple microservice!\nHowever, you still need to think about where to store the identifier. Additionally, assigning an identifier to a user or device can be challenging. You will need a central instance to manage these identifiers and assignments in the end.Fortunately, Firebase Authentication provides this functionality out of the box.\nProbably all major programming languages have built-in support for UID generation and you could even generate it in your mobile app. But it’s not a good idea to let the client create this vital identifier. Better do it in the backend!\nConclusion In this article, I showed you my approach of how to connect data between different Firebase services. Unique identifiers are an easy way to accomplish this without too much trouble.\n","permalink":"https://quickcoder.org/posts/how-to-connect-data-between-different-firebase-services/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eFirebase offers several services for storing data, including Firestore, Storage, and Realtime Database. However, it can be difficult to keep track of which data belongs together across service borders. Use my approach on how to connect data between different Firebase services. It has worked great for me in the past!\u003c/p\u003e\n\u003ch1 id=\"unique-indentifiers-uid\"\u003eUnique indentifiers (UID)\u003c/h1\u003e\n\u003cp\u003eWhen using Firebase Authentication, you get a UID per user. We can use this UID to identify corresponding data sets that belong to this user across services.\u003c/p\u003e","title":"How To Connect Data Between Different Firebase Services"},{"content":"Introduction We\u0026rsquo;ve all been there, cursing because of CORS. You make an HTTP request only to be greeted by a frustrating error message in your browser. With this short article, I want to show you how to fix CORS errors in web applications.\nWhat is CORS? Cross-Origin Resource Sharing (CORS) is a mechanism that allows web browsers or other web clients to make cross-origin requests, which are normally prohibited by the Same-Origin Policy (SOP). CORS is a compromise that provides greater flexibility on the Internet while maintaining high security measures. The Same-Origin Policy (SOP) is a security feature implemented by web browsers that restricts web pages from making requests to a different origin than the one that served the web page. In simple terms, SOP prevents malicious web pages from stealing data or executing actions on behalf of a user without their knowledge or consent.\nTheory CORS is basically a client asking a server\nHey, I know I am not from here but I would like to read (or write) some data, can I do that?\nAnd the server responds with\nYeah, my admin already told me that guys like you might come by, so it\u0026rsquo;s okay.\nSo it’s a server configuration issue and if you don’t control the server, you can’t fix the problem!\nCORS error when requesting resources with a different origin and incorrect server configuration\nBut how to fix it? Simple, just add the header Access-Control-Allow-Origin to your server configuration\naccess-control-allow-origin: * This means that requests from any origin are allowed. You can either specify a single origin URL or use the * value as a wildcard.\nSometimes, you also want to restrict the allowed HTTP methods that clients can use. The header field Access-Control-Allow-Methods is your friend.\naccess-control-allow-methods: GET, PUT, OPTIONS The specific steps for adding these header fields depend on the type of server you are running. However, you can find the answer on the internet, and ChatGPT may even be able to assist you with that.\nConclusion In this article, you learned how to fix CORS errors in web applications. Once you know the theory behind the scenes, it\u0026rsquo;s easier to understand and won\u0026rsquo;t cause you big trouble in the future.\n","permalink":"https://quickcoder.org/posts/how-to-fix-cors-errors/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eWe\u0026rsquo;ve all been there, cursing because of CORS. You make an HTTP request only to be greeted by a frustrating error message in your browser. With this short article, I want to show you how to fix CORS errors in web applications.\u003c/p\u003e\n\u003ch1 id=\"what-is-cors\"\u003eWhat is CORS?\u003c/h1\u003e\n\u003cp\u003e\u003ca href=\"https://en.wikipedia.org/wiki/Cross-origin_resource_sharing\"\u003eCross-Origin Resource Sharing\u003c/a\u003e (CORS) is a mechanism that allows web browsers or other web clients to make cross-origin requests, which are normally prohibited by the Same-Origin Policy (SOP). CORS is a compromise that provides greater flexibility on the Internet while maintaining high security measures. The \u003ca href=\"https://en.wikipedia.org/wiki/Same-origin_policy\"\u003eSame-Origin Policy\u003c/a\u003e (SOP) is a security feature implemented by web browsers that restricts web pages from making requests to a different origin than the one that served the web page. In simple terms, SOP prevents malicious web pages from stealing data or executing actions on behalf of a user without their knowledge or consent.\u003c/p\u003e","title":"How to Fix CORS Errors in Web Applications"},{"content":"Introduction The Firebase Console is the central hub for all Firebase services. Additionally, there are also some project management tasks that can be done with it.\nThe ultimate guide for working with the Firebase Console explains everything you need to know about the following use cases:\nAdding apps\nConnect iOS, Android, Web, Unity, or Flutter apps to your Firebase project.\nRemoving apps\nRemove connected apps again.\nManaging users\nInvite new people, change their roles, or remove them from the project.\nSwitching billing plans\nPrevent costs with the free plan or enable all features and pay for what you use.\nMonitoring expenses\nKeep track of what service generates what costs.\nCreating budget alerts\nGet notified when your budget is exceeded.\nDeleting a Firebase project\nIn case you like Azure or AWS more ?\nAdd apps to a Firebase project Adding apps to a Firebase project can either be done with the Firebase Console or with the Firebase CLI tools. Here are both ways, so you can choose your preferred one.\nFirebase Console In the Firebase Console, click on the gears icon next to the Project overview menu entry in the Firebase menu on the left.\nAdd new Firebase app step 1: Click on the gears icon in the Firebase Console menu on the left.\nThen, select Project settings from the popup menu.\nAdd new Firebase app step 2: Click on the Project settings menu entry.\nAt the bottom you can find your existing apps grouped by platform.\nA list of all existing Firebase apps in your current Firebase project.\nTo add a new app, click on the button Add app, choose your desired platform, and follow the setup instructions. After that, a new app appears in your list.\nAdd new Firebase app step 3: Scroll down to the bottom and click on the button Add app.\nAdd new Firebase app step 4: Choose the desired app platform. The available options are iOS, Android, Web, Unity, and Flutter.\nAdd new Firebase app step 5: Follow the instructions of the setup guide.\nThe available platforms are iOS, Android, Web, Unity, and Flutter.\n💡Tip\nWhen you use Flutter, the corresponding iOS, Android, and Web apps will be created automatically. You don’t need to perform the manual steps in the setup guide.\nFirebase CLI tools The Firebase CLI tools offer a way to add new apps to an existing Firebase project. To do so, first log in and select the project to which you want to add apps.\nfirebase login firebase projects:list firebase use \u0026lt;project id\u0026gt; The firebase login command opens a browser window for authentication. After that, you can get a list of all your projects with the firebase projects:list command. Copy a project id and pass it to the firebase use command to set the active project.\nAdd new Firebase app with Firebase CLI step 1: Log in your Firebase account with the command firebase login. Then, list your projects with firebase projects:list to get the project ids. Eventually, set the active project with the firebase use command and pass the corresponding project id to it.\nTo create a new project, use the command firebase apps:create and follow the instructions of the assistant.\nAdd new Firebase app with Firebase CLI step 2: Create a new app with the command firebase apps:create. Choose your desired platform and give the app a name.\n💡Tip\nThe Firebase CLI only supports iOS, Android, and Web as target platforms!\nRemove apps from a Firebase project In the Firebase Console, click on the gears icon next to the Project overview menu entry in the Firebase menu on the left.\nRemove Firebase apps step 1: Click on the gears icon in the Firebase Console menu on the left.\nThen, select Project settings from the popup menu.\nRemove Firebase apps step 2: Click on the Project settings menu entry.\nAt the bottom you can find your existing apps grouped by platform.\nA list of all existing Firebase apps in your current Firebase project.\nTo delete an existing app, select the app from the list and click on the Remove this app button located at the bottom of the page. A confirmation window will appear and once confirmed, the app will be deleted.\nRemove Firebase apps step 3: Scroll down to the bottom and click on the button Remove this app.\nRemove Firebase apps step 4: Confirm the removal by clicking on Remove app permanently\n⛔ Error\nYou cannot remove Firebase apps with the Firebase CLI tools. The only way is to use the Firebase Console as described above.\nManage users in a Firebase project In the Firebase Console, click on the gears icon next to the Project overview menu entry in the Firebase menu on the left.\nManage Firebase users step 1: Click on the gears icon in the Firebase Console menu on the left.\nThen, select Users and permissions from the popup menu.\nManage Firebase users step 2: Click on the Users and permissions menu entry.\nIn this menu you can search for users, add new ones, or edit/remove existing ones.\nUse the search field at the top to do a full text search for users. You can enter names, part of the email address, or even search for users with certain rolls.\nA list of all existing users in a Firebase project.\nTo edit an existing user, click on their name. The row will expand, and you can then edit the user\u0026rsquo;s role using a dropdown menu.\nManage Firebase users step 3: Click on a user’s name to edit their role from a dropdown menu.\nTo remove an existing user, hover over their name and click on the three dots that appear on the right. Then, select the option Remove member. After you confirm the action, the user will be removed and an email will be sent to them.\nManage Firebase users step 4: Hover over a user’s name, click the three dots on the right, and select Remove member. Confirm the action and the user will be removed afterward.\nTo add a new member, click on the button Add member in the top-right corner. Enter an email address and select a role. You can either choose one of the predefined self-explanatory roles like Owner, Editor, or Viewer or you can assign Firebase roles individually.\nThe user will receive an email and can access this Firebase project according to the defined role.\nManage Firebase users step 5: Click on the button Add member, enter an email address, set a role, and click on the second Add member button. The user will receive an email and can join the Firebase project.\nYou can also add new users using the Google Cloud Console. While it may appear more complicated than the Firebase UI, it offers more options and allows you to grant rights in greater detail. Just click on the link Advanced permission settings at the bottom to get to the Google Cloud Console.\nManage Firebase users step 6: For advanced permission settings in Google Cloud click the link Advanced permission settings and manage your users with a more powerful UI.\nChange the current billing plan of a Firebase project Firebase supports two types of billing plans: Spark and Blaze. Each billing plan covers one Firebase project, so you can have different projects on different plans. By default, you are on the Spark plan.\nWhen using the Spark plan, you won\u0026rsquo;t have to pay anything for Firebase ever. It\u0026rsquo;s a free version with a limited resource quota that is suitable for testing and development. However, if you cross a service\u0026rsquo;s limit, it won\u0026rsquo;t work until the next billing period. Additionally, some services like Cloud Functions or Extensions cannot be used.\nWith the Blaze plan, you are charged based on usage. The more you use, the more you pay. All Firebase services are available, but you can still take advantage of the free quota. If you don\u0026rsquo;t use any resources, you won\u0026rsquo;t be charged anything.\nIt is possible to switch billing plans at any time.\nUpgrade from Spark to Blaze By upgrading to the Blaze plan, you can instantly access all Firebase services while still benefiting from the free quota of these services.\nStart by going to the Firebase Console and clicking on Upgrade in the bottom left corner.\nFirebase plan upgrade step 1: Click on the Upgrade button in the bottom left corner of the Firebase menu\nThen click on the Select plan button of the Blaze plan.\nFirebase plan upgrade step 2: Click on Select plan in the Blaze column to upgrade to the Blaze plan.\nNext, you need to select a billing account. If you do not already have one, you can create a billing account here. After creating a billing account, select it from the dropdown menu and click on Continue.\nFirebase plan upgrade step 3: Choose your billing account in the dropdown menu and click on Continue. If you don’t have a billing account, you can create one here.\nYou can set a budget here and receive email notifications when your costs approach the threshold. However, these notifications are only meant as a reminder, and you will need to take action accordingly. This step is optional and can be completed at a later time.\nFirebase plan upgrade step 4: If you want to get notified via email when your expenses exceed a threshold you can set up a budget alert in this step. However, this is optional and you can do it at any time in the future.\nConfirm the upgrade by clicking on Purchase.\nFirebase plan upgrade step 5: Review the upgrade and confirm with a click on Purchase.\nYour upgrade process is completed and you will get an email as notification.\nFirebase plan upgrade step 6: The upgrade was successful. A notification email has been sent to you.\nDowngrade from Blaze to Spark A downgrade to the Spark plan will prevent access to service like Cloud Functions. Data loss is also possible when your stored files exceed the free quota. So make sure to only downgrade when you have saved your important data. On the other side, your costs won’t increase anymore.\nStart by going to the Firebase Console and clicking on Modify in the bottom left corner.\nFirebase plan downgrade step 1: Click on the Modify button in the bottom left corner of the Firebase menu\nThen click on the Select plan button of the Spark plan.\nFirebase plan downgrade step 2: Click on Select plan in the Spark column to downgrade to the Spark plan.\nRead the details of the downgrade. Make sure that you saved all relevant data because billable resources might be deleted after the downgrade. Confirm the process by writing Downgrade in the text box and click the button.\nFirebase plan downgrade step 3: Review the changes and confirm the process by writing Downgrade in the text box. A final click on the Downgrade button completes the process.\nThe downgrade process is completed. You will receive a notification email.\nFirebase plan downgrade step 4: Your project has been downgraded to the Spark plan and an email confirmation has been sent to you.\nMonitor the current costs of a Firebase project In the Firebase Console, click on the gears icon next to the Project overview menu entry in the Firebase menu on the left.\nFirebase Expense Monitoring step 1: Click on the gears icon in the Firebase Console menu on the left.\nThen, select Usage and billing from the popup menu.\nFirebase Expense Monitoring step 2: Click on the Usage and billing menu entry.\nThe first row displays the current expenses for this month. As a Spark plan user, everything is free and won\u0026rsquo;t cost you anything. However, if you exceed the free limits, the services will stop working.\nFor each service, you can see an exact overview of the resources used. This allows you to easily identify areas where you may reach the limits in the current period. Note that data updates occur every 24 hours. This is not a live dashboard!\nFirebase Expense Monitoring step 3: Here is an overview of all your services and used resources.\nTo view historical data, select a different month from the dropdown menu in the upper right corner. Please note that while you can see the costs, you will not be able to view the used resources.\nFirebase Expense Monitoring step 4: To view older data use the dropdown menu on the right to select another billing period. However, you can only see the costs and not the used resources of older periods.\nIf you want to receive notifications when costs exceed a certain threshold, consider setting up a budget alert. In this way, you can take countermeasures to reduce costs.\nCreate budget alerts for Firebase projects In the Firebase Console, click on the gears icon next to the Project overview menu entry in the Firebase menu on the left.\nFirebase budget alert step 1: Click on the gears icon in the Firebase Console menu on the left.\nThen, select Usage and billing from the popup menu.\nFirebase budget alert step 2: Click on the Usage and billing menu entry.\nNext, switch to the Details and settings tab.\nAt the bottom you can create a budget alert by clicking on Create first budget. You will be redirected to the Google Cloud Console where all alerts are managed.\nFirebase budget alert step 3: Click on Create first budget at the bottom to create a budget alert in the Google Cloud Console.\nFill out the relevant fields of the scope, define a target amount, and manage additional thresholds as you finish. The only required fields are the name and the amount. All other fields are optional. After a click on the Finish button, your budget alert is active. You can see it in your Firebase Console.\nFirebase budget alert step 4: Define the scope of the budget alert. By default, all services are selected and it is enough to enter a name for the budget. Continue by clicking on the Next button.\nFirebase budget alert step 5: Enter a target amount for your budget. On the right you can see your current costs for orientation. Click on the Finish button to complete the process or the Next button to define the alerting process in detail. By default, you will receive an email when your costs come near the threshold.\nFirebase budget alert step 6: In this optional step you can define further alerting thresholds and other options. Click on the Finish button after the changes.\nFirebase budget alert step 7: Your budget alert was successfully created.\nYou can also create a budget alert when switching from the Spark to the Blaze plan.\nDelete a Firebase project In the Firebase Console, click on the gears icon next to the Project overview menu entry in the Firebase menu on the left.\nDelete a Firebase project step 1: Click on the gears icon in the Firebase Console menu on the left.\nThen, select Project settings from the popup menu.\nDelete a Firebase project step 2: Click on the Project settings menu entry.\nAt the bottom of the page, there is a Delete project button. Clicking on it will open a confirmation window, where you need to select multiple checkboxes before finally deleting the project.\nDelete a Firebase project step 3: Scroll to the bottom of the page and click on Delete project.\nDelete a Firebase project step 4: Activate all checkboxes and confirm the deletion by clicking the button Delete project.\n⛔ Error\nYou cannot delete a Firebase project with the Firebase CLI tools. The only way is to use the Firebase Console as described above.\nConclusion With the ultimate guide for working with the Firebase Console you should be able to cover all management tasks for Firebase projects.\n","permalink":"https://quickcoder.org/posts/ultimate-guide-for-working-with-the-firebase-console/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eThe Firebase Console is the central hub for all Firebase services. Additionally, there are also some project management tasks that can be done with it.\u003c/p\u003e\n\u003cp\u003eThe ultimate guide for working with the Firebase Console explains everything you need to know about the following use cases:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eAdding apps\u003c/strong\u003e\u003cbr\u003e\nConnect iOS, Android, Web, Unity, or Flutter apps to your Firebase project.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eRemoving apps\u003c/strong\u003e\u003cbr\u003e\nRemove connected apps again.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eManaging users\u003c/strong\u003e\u003cbr\u003e\nInvite new people, change their roles, or remove them from the project.\u003c/p\u003e","title":"The Ultimate Guide for Working with the Firebase Console!"},{"content":"Introduction During the Google I/O 2023 developer conference, Google announced countless new features and changes for Firebase. Several new AI-powered extensions have been published since then. Here is what the new firebase AI extensions can do for your apps.\nAI-powered extensions added to Firebase Extension Hub\nChatbot with PaLM API The Chatbot with PaLM API extension provides an AI-powered chatbot that can answer user prompts like ChatGPT. The extension uses Cloud Firestore and listens for new documents in a configurable collection. Once a valid document is available, the AI generates an answer via Cloud Functions and writes the result back to the document. This extension is useful for developers who want to add chatbot functionality to their apps without building the entire infrastructure from scratch.\n💡If you need a chatbot for your app or website, then this is the way to go!\nSummarize Text with PaLM API The Summarize Text with PaLM API extension allows developers to summarize text using the PaLM API. The AI generates a summary of the text, and the developer can use the results to provide text summarization functionality in their apps. Just store the text in a Firestore document and the extension will do the rest.\n💡Make long texts shorter for your users with this extension!\nLanguage Tasks with PaLM API The Language Tasks with PaLM API extension is a universal extensions that accepts any prompt and creates a response for it. It works with a Cloud Firestore collection to read prompts and write responses while a Cloud Function calls the AI model. Anything that you know from ChatGPT prompts also works here.\n💡Want to see if customer feedback is positive or negative? No problem for this extension!\nExtract Image Text with Cloud Vision AI The Extract Image Text with Cloud Vision AI extension grabs text from inside images. Upload an image to Firebase Storage, tell the extension where it is, and the AI writes the containing text to a Cloud Firestore document.\n💡Get menu items from restaurant images or read text from social media images!\nConvert Text to Speech When you want to convert an article into a Youtube video, the Convert Text to Speech extension can generate an audio file from the text content. Write the text to a Cloud Firestore document. The extension creates the corresponding audio file in a Firebase Storage bucket and writes the path in your document.\n💡Podcasts, Youtube videos, or virtual assistants rely on this feature!\nTranscribe Speech to Text To convert audio file into text, use the Transcribe Speech to Text extension. It expects an audio file in a Firebase Storage bucket and writes the transcribed text to a Cloud Firestore document.\n💡With this extension and Summarize Text with PaLM API, you can create useful Podcast summaries for your users!\nLabel Videos with Cloud Video AI The Label Videos with Cloud Video AI extension extracts labels from media files stored in Cloud Storage and writes them as JSON files back to Cloud Storage. The supported formats are MOV, MPEG4, MP4, and AVI.\n💡Content moderation or a recommendation system can be implemented with this extension!\nConclusion With these extensions, you can handle complex implementation tasks in no time. They save you from coding complex algorithms. Use them to get your apps done faster.\n","permalink":"https://quickcoder.org/posts/firebase-ai-extensions/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eDuring the Google I/O 2023 developer conference, Google announced countless \u003ca href=\"https://medium.com/gitconnected/new-firebase-features-introduced-at-google-i-o-2023-d7b7e572cc39\"\u003enew features and changes for Firebase\u003c/a\u003e. Several new AI-powered extensions have been published since then. Here is what the new firebase AI extensions can do for your apps.\u003c/p\u003e\n\u003cfigure\u003e\n\u003cp\u003e\u003cimg alt=\"AI-powered extensions added to Firebase Extension Hub\" loading=\"lazy\" src=\"/posts/firebase-ai-extensions/images/2023-07-10-21_42_49-Window.webp\"\u003e\u003c/p\u003e\n\u003cfigcaption\u003e\n\u003cp\u003eAI-powered extensions added to Firebase Extension Hub\u003c/p\u003e\n\u003c/figcaption\u003e\n\u003c/figure\u003e\n\u003ch1 id=\"chatbot-with-palm-api\"\u003eChatbot with PaLM API\u003c/h1\u003e\n\u003cp\u003eThe Chatbot with PaLM API extension provides an AI-powered chatbot that can answer user prompts like ChatGPT. The extension uses Cloud Firestore and listens for new documents in a configurable collection. Once a valid document is available, the AI generates an answer via Cloud Functions and writes the result back to the document. This extension is useful for developers who want to add chatbot functionality to their apps without building the entire infrastructure from scratch.\u003c/p\u003e","title":"What The New Firebase AI Extensions Can Do For Your Apps"},{"content":"Introduction Here are my observations after extensively testing and building feature-rich apps using both Google and Microsoft frameworks.\nFor the past few years, I have been developing both small and large Flutter applications. Flutter is an amazing framework that allows for the quick development of cross-platform apps. Initially, it was just another tool for me to experiment with, but now it\u0026rsquo;s my primary choice for new mobile projects without any hesitation.\nI love Flutter. About half of my articles are tutorials on how to accomplish tasks with Flutter. They are a compendium of what I have learned and struggled with in the past.\nAs a .NET developer, I find it surprising that I never took the time to explore Xamarin for mobile development. However, .NET MAUI is slowly gaining traction, which is essentially a rebranded version of Xamarin. I tested it about a year ago and was astonished by its poor state, as I wrote about in this article. Today, I realize that I maybe didn\u0026rsquo;t try hard enough back then.\nNevertheless, I experimented with it and as a seasoned .NET dev, I instantly felt at home. It’s like riding a bicycle. C#, Visual Studio, MVVM, Nuget, all that works here as well. It’s cool to know that your existing knowledge can open doors to new platforms with a new framework.\nThe big question now is: Is .NET MAUI better than Flutter?\nI’d like to give you my opinion on that. And please, tell me yours. But now, let’s go!\nWhat I think about Flutter ✅ Huge amount of widgets built into the SDK to create user interfaces.\n✅ You only need one programming language for UI and business logic, which is Dart. Some people say it\u0026rsquo;s not a good language, but it has received many updates in the past and is constantly evolving.\n✅ Several major updates per year, clear roadmap, backed by Google.\n✅ Targets iOS, Android, Windows, macOS, Linux, web, and embedded platforms.\n✅ Hot Reload/Hot Restart feature works well.\n✅ Rising marketshare, loved by developers.\n✅ It feels like you get things done quickly.\n✅ Huge package repository with many well-maintained entries.\n⚠️ Build time could be faster\n⚠️ File size of apps could be smaller\n⚠️ Bad app performance on web compared to iOS and Android\n⚠️ Setting up can be difficult since additional tools are required for the platforms. However, the documentation is detailed and will guide you through the process.\n⚠️ Frequent updates means chances for breaking changes are high.\n⚠️ Platform features need to be implemented by oneself if they are not part of the framework or adding via a 3rd party package.\n⚠️ Many open issues on GitHub, but also bigger development team that puts in the effort needed.\n⛔ State management can be messy. There are so many options, frameworks, and levels of flexibility, but no state-of-the-art solution has been found yet.\nWhat I think about .NET MAUI ✅ C#, XAML, and Visual Studio are great. They may not be the absolute best or the fastest, but they have demonstrated their capabilities over the years.\n✅ For experienced WPF or UWP developers, .NET MAUI will feel instantly familiar. They already know basic concepts like creating UIs or controls, how data-binding works, working with MVVM, and useful libraries.\n✅ The Fluent design looks nicer than Material design or Apple design. Of course, you can implement any design system on any platform, but the .NET MAUI look and feel wins out of the box for me.\n✅ You have access to a huge number of Nuget packages. Everything from .NET Standard 2.0 can be used in general.\n✅ Sufficient amount of controls provided by default. Via Nuget you have access to many others like those of the .NET MAUI Community Toolkit.\n✅ Good documentation about the framework.\n✅ Tooling setup is super easy. Download Visual Studio, select the .NET MAUI workload, and wait for the installation to finish. That’s it.\n⚠️ XAML Hot Reload is quite nice, but when you make specific changes, the app needs to be recompiled completely. Also, it doesn’t work when you edit C# code.\n⚠️ If you are familiar with WPF or UWP, you will notice the similarities and the differences. And they will drive you crazy in the beginning.\n⛔ Build time is too high. Since the Hot Reload feature is not perfect, frequent building is required. It will become annoying over time, I promise.\n⛔ There are lots of open bugs in the official repository. I already criticized this about a year ago, and although a lot has been happening, there seem to be even more annoying bugs arising, especially for Android at the moment.\n⛔ No linux support. How can you claim to be cross-platform when Linux is missing? But yeah, there’s Tizen. And who doesn’t use that?\n⛔ Beginners won’t choose it for quick prototyping when having little experience or when special controls are needed. You can get things done quicker with other frameworks.\nConclusion If I had no prior experience with either framework, I would choose Flutter. Although the setup process can get a bit messy, the perceived development speed is much faster than with .NET MAUI. However, I joined the Microsoft ecosystem over 20 years ago and I still enjoy writing apps with .NET and C#. I also appreciate their progress in the last year as .NET MAUI is definitely in better condition now.\n","permalink":"https://quickcoder.org/posts/flutter-vs-net-maui/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere are my observations after extensively testing and building feature-rich apps using both Google and Microsoft frameworks.\u003c/p\u003e\n\u003cp\u003eFor the past few years, I have been developing both small and large Flutter applications. Flutter is an amazing framework that allows for the quick development of cross-platform apps. Initially, it was just another tool for me to experiment with, but now it\u0026rsquo;s my primary choice for new mobile projects without any hesitation.\u003c/p\u003e","title":"Flutter vs .NET MAUI: Which Is Better For Creating Cross-Platform Apps?"},{"content":"Introduction Here\u0026rsquo;s how you can animate your Flutter widgets to make your app stand out from the rest with ease. Make your apps look nicer and present your users a better UX with animations!\nAnimations are the icing on your app cake. Fortunately, the Flutter team has put a lot of effort into making animations as easy as possible. In this article, I\u0026rsquo;ll show you how it\u0026rsquo;s done. I\u0026rsquo;ll also provide a sample application at the end, so you can try out everything discussed in this guide.\nAnimation widgets There are already many widgets available in the framework that are specifically designed to use animations. You can find them in the Flutter widget index, and they all start with the prefix Animated. We will discuss the following ones:\nAnimatedAlign AnimatedContainer AnimatedCrossFade AnimatedDefaultTextStyle AnimatedOpacity AnimatedPhysicalModel AnimatedPositioned AnimatedSize But wait\u0026hellip; there are more animation widgets in the index list. Why don\u0026rsquo;t we cover these as well?\nThe widgets in my list work similarly and are super easy to use. The other ones require more setup and code. Sometimes, you even have to test the entire animation yourself. We\u0026rsquo;ll cover the advanced stuff in a future article.\nHow do these widgets work? Let’s take the AnimationContainer as an example. If you use a normal Container widget and you change a property like its height, the new height will be visible immediately after a rebuild.\nAnimation widget however will not jump to the final value but set values between the starting and the final value. The values are influenced by the duration of an animation and the selected Curve function.\nYou don\u0026rsquo;t have to worry about intermediate values or frame rates to make your animation smooth. Just set a desired value, choose a duration, and select an animation curve. The widget will do the rest. It’s really simple!\nIf you know how one widget works, you also know how the others work!\nBelow is some code to show you how it essentially works:\ndouble _width = 300; AnimatedContainer( curve: Curves.linear, width: _width, // this is the value that will be animated when changed height: 300, duration: const Duration(seconds: 1), child: Container()); ... setState((){ _width = 400; // set a new value and trigger a rebuild }); What can I animate with these widgets? Depending on what you want to do, you need to choose the right widget. The widget names sometimes already tell what you can animate with them. Here is a short overview what every widget can do. Check the documentation for exceptions and pitfalls.\nAnimatedAlign supports the Alignment property (for example Alignment.topLeft to Alignment.bottomRight). AnimatedContainer supports many properties like Alignment, Padding, Width, Height, Color, or BorderRadius. AnimatedCrossFade has two properties firstChild and secondChild. It will animate between these widgets when the crossFadeState property is changed. AnimatedDefaultTextStyle supports the style property to animate TextStyle changes. AnimatedOpacity supports the opacity property. It should be between 0 (invisible) and 1 (completely visible). AnimatedPhysicalModel supports the elevation property. AnimatedPositioned supports the width, height, top, left, bottom, and right properties, but it only works inside a Stack widget. AnimatedSize supports the size property Have a look at my demo application to see all these widgets in action.\nCurves A curve defines changes in value over time. This is how your animation will look. With a linear curve, your animation will look smooth without any gaps or jumps. However, there are also curves that start slow and get faster at the end, for example. The documentation provides sample videos for every predefined curve. With so many options, you won\u0026rsquo;t feel the need to write your own curve.\nAll presented widgets have a Curve property that can be used to specify the desired animation function.\nConclusion The Flutter team made animations really easy with so many predefined widgets. With this guide, you should be able to handle them. If you want to play around with the different widgets and animations functions, check out my demo application on GitHub.\nDemo video of the Flutter app to try out animation widgets and functions\n","permalink":"https://quickcoder.org/posts/flutter-animations/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere\u0026rsquo;s how you can animate your Flutter widgets to make your app stand out from the rest with ease. Make your apps look nicer and present your users a better UX with animations!\u003c/p\u003e\n\u003cp\u003eAnimations are the icing on your app cake. Fortunately, the Flutter team has put a lot of effort into making animations as easy as possible. In this article, I\u0026rsquo;ll show you how it\u0026rsquo;s done. I\u0026rsquo;ll also provide a sample application at the end, so you can try out everything discussed in this guide.\u003c/p\u003e","title":"How To Use Animations And Animated Widgets In Flutter Apps"},{"content":"Introduction I built a WhatsApp clone with Flutter and Firebase in a few hours. Here is what I came up with.\nThere are a ton of messaging apps available. Some well-known players are WhatsApp, Threema, Signal, and others. Apart from a messaging functionality, they provide many other features like group chats, file transmission, or data backup. In my little experiment, I tried to recreate the messaging feature that all apps use. My requirements were the following:\nTwo parties can send messages to each other The user interface should look similar to other messaging apps The client app is written with Flutter The backend is Google Firebase User management I created basic data models in the beginning. They contain all the information that is relevant to the messaging system. First, we have a ChatUser object. It has an identifier, a name, and a list of chat identifiers. Why chat identifier? I’ll get to that later.\nFor authentication, I went with Firebase Authentication and its email/password authentication combination. It’s easy to implement and ensures that only registered users can write to my database. Uncontrolled data access can lead to huge bills! If you want even more security for your backend, try Firebase App Check!\nView to register a new user with Firebase Authentication in the messaging system.\nView to log in an existing user with Firebase Authentication in the messaging system.\nThe first screen is the login/register screen. The entire authentication is handled by Firebase Authentication. At no point, I can see the password! Chatting just requires a name that is displayed to other parties and an identifier to find the correct ChatUser object.\nHere is the code to log in and register. In both cases, the Firestore collection users is accessed. During login, the matching ChatUser object is grabbed while during registration, the ChatUser object is created.\nFirestore collection with 3 users and their properties\nThis is how the users collection looks like in Firebase Firestore. The user identifier is also the document identifier which makes it easier to add filter clauses to the Firestore query. If I want to find a user with a certain id, I can look for the document with that id.\nListing and creating chats After the login, the chat overview is loaded. It contains all chats and a button to start a new chat.\nOverview of created chats in the Flutter Firebase Chat app\nA Chat object is pretty simple, it contains an identifier, a list of ChatUser objects, and a list of ChatMessage objects. The identifier is the concatenation of the identifiers of the users. And this chat identifier is also stored in each ChatUser object.\nWhen a new chat is created, then a new entry is added to the chats collection in Firestore. Also, the ChatUser objects are updated because their chat identifier collection gets a new entry.\nChats collection in Firebase Firestore containing an identifier, a list of messages, and a list of users (not in image).\nWhen a User logs in and arrives at the chat overview, I use the list of chat identifiers that is stored alongside a User object to create streams. These streams update immediately and the user can see the latest message of every chat in the overview. I don’t want to load the entire chat message history yet but only the stream for fast updates. That’s why I decided to store chat identifiers with a User.\nWriting messages Writing messages is pretty easy since we have all the information we need. The SingleChatPage is attached to the stream of the chat collection with the given identifier. You can use a StreamBuilder widget for that purpose. It also takes care to dispose of the stream for you.\nAdding a new message is reduced to adding a new message entry to the Chat object. First, I create it from the Firestore collection. Then, I add a new entry. And last but not least, I call the update method of Firestore to publish the changes.\nSince the UI is attached to the chat stream, it reflects changes in an instant. Below you can see a screenshot of what it looks like:\nSending and receiving messages with a chat partner in Flutter Firebase Chat app.\nWhat does it look like? Here is a video of the final example. In the background, you can see the database. I am writing messages from the app on my phone and have another instance running in the browser (left area of the video). As you can see, the chat is immediately updated when the database field is updated.\nA Flutter Firebase Chat App demo video. The right area shows the Firebase Firestore documents and the left area is a running app instance in the browser.\nThe next steps would be to add further functionality, eliminate some bugs, and improve the UI. In general, it is similar to known chat apps but it looks like it is a prototype. However, the goal was to implement a WhatsApp clone with Firebase and not make it a feature-complete and production-ready mobile app for every platform.\nPossible enhancements Here are some improvements that can be implemented to make the app even more useful. I did not do it because it wasn’t on the agenda. Feel free to get inspired.\nFile and image transfer\nSending images and files is a crucial part of every messenger app today. This app can be easily enhanced to support these features by using Firebase Storage.\nProfile pictures\nAt the moment, users can only select their username during the registration process. Other personalization features are not implemented right now. Firebase Storage is also a perfect solution for this purpose.\nGroup chats\nThis is also a frequently used feature. By design, the app supports group chats. It’s just not possible to select more than one chat partner currently.\nPush notifications\nGet notified when a new message was posted in a chat. A very useful feature that can be implemented in various ways. Firebase offers this capability, too. Here’s my full guide to implementing notifications:\nPaging\nWhen a chat is opened, all messages are downloaded. This might get slow if the message numbers grow. With paging, the bottleneck can be fixed. Paging means that only the latest chunk of information is retrieved. Previous data will only be grabbed if explicitly requested.\nSound notifications\nMessengers come with sound notifications for new messages, file transfers, or incoming calls. This could be implemented by adding small audio files to the Flutter project and playing them at the right moments. I don’t suggest storing them in the cloud or accessing them through HTTP requests because the delay might be high in some cases.\nBugs The code is not bug-free. For example, if you start a new chat and send a first message, the UI is not updated. This is because there is no stream yet to subscribe to. There are also some bugs with the navigation when creating a new chat.\nAs it was an experiment, I didn’t invest much time in improving the user experience. But that can easily be fixed by anyone.\nConclusion Creating a messaging app like WhatsApp with Flutter and Firebase was easier than I thought. In fact, I spent most of the time with the user interface and not with the messaging logic or the Firebase backend. And it was a lot of fun. Of course, it’s not suited for a large user base but the concepts are quite clear to anybody.\nSource code You can find the source code on GitHub. The repo contains the working app without the Firebase configuration. You need to create the account and link your app with it. Then you can reproduce all the results of this article.\n","permalink":"https://quickcoder.org/posts/flutter-firebase-chat/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eI built a WhatsApp clone with Flutter and Firebase in a few hours. Here is what I came up with.\u003c/p\u003e\n\u003cp\u003eThere are a ton of messaging apps available. Some well-known players are WhatsApp, Threema, Signal, and others. Apart from a messaging functionality, they provide many other features like group chats, file transmission, or data backup. In my little experiment, I tried to recreate the messaging feature that all apps use. My requirements were the following:\u003c/p\u003e","title":"Firebase WhatsApp Clone Built With Flutter in No Time!"},{"content":"Introduction Save coding time by using Firebase Extensions and create an URL shortening service in minutes! Here’s the full guide.\nWith a few simple steps, you can build your own personal URL shortening service. All you need is a Firebase account and a Bitly account. The setup involves no coding. To test the service, we will write a small Flutter app.\nFirebase offers a variety of extensions. The major benefit is that you gain functionality without having to code anything yourself. I already wrote a detailed article about it if you need more details.\n💡Hint\nBe aware that you might run into some costs. An installed extension costs around 1 cent per month and it normally uses other services in the background which can increase these costs. You will also need to activate the Blaze plan.\nBitly Setup First, create a free Bitly account. Next, generate an access token that we can use to access the service from Firebase.\nLog into your account. Then go to Settings → API. Enter your password and hit the button to generate an access token. Copy the token as it will disappear when you reload the page. See the image below for details.\nHow to create an access token in your bitly account dashboard\nWe have our access token which is required by Firebase. Bitly allows the creation of 10 short URLs per month with the free plan.\nFirebase Setup First, we need to install the required extension. Go to Build → Extensions.\nScreenshot of Firebase Extensions menu entry by author\nNext, hop to the marketplace, find the extension Shorten URLs, and install it.\nInstalling the Shorten URLs extension from the Firebase marketplace\nYou might need to enable some Google Cloud features to make the extension work.\nEnabling Cloud Functions and Secret Manager for the extension Shorten URLs\nDuring the last step, you can configure the extension. Don’t worry, you can change the values later (except for the Cloud Functions location).\nConfigure the Shorten URLs extension of Firebase\nSo, what values are required here exactly?\nCloud Functions location indicates where the server is located that handles calling Bitly to shorten a url and writing the result back to Cloud Firestore. Check out my article about Cloud Functions and especially the section Regions for more details. Bitly access token expects the access token that you created earlier Collection path indicates the path inside the Cloud Firestore NoSql database URL field name is the name of the field where the long form url is stored Short URL field name is the name of the field where the short form url is stored You can use the default values, but you need to provide the Bitly access token!\nHit install and wait a few minutes until the process is completed!\n💡Hint\nWhat happens behind the scenes? When the field value changes, a cloud function is triggered (you can see it in the Cloud Functions Dashboard of Firebase). It calls the Bit.ly service to shorten the given url and writes the result back to the Cloud Firestore document.\nVerify Firebase Setup You can see that a function was deployed in your Cloud Functions dashboard.\nDeployed function to shorten URLs in Firebase\nHead over to Cloud Firestore, create a new collection (click on Start collection), and a document containing the two fields according to your configuration. The document ID can be auto-generated.\nCreate a new collection in Firebase Cloud Firestore\nCreate a new document in Firebase Cloud Firestore\nThe Firebase setup is completed. Let’s write a simple Flutter app to make use of our workflow.\nApp Setup We need to create an app that writes text to Cloud Firestore and listens to changes which it will then display to the user.\nStep 1: Create a Flutter project.\nUse the command flutter create my_sample_project or your IDE of choice for it.\nStep 2: Link it with your Firebase project.\nRun flutterfire configure from the project root folder and follow the wizard.\nStep 3: Install Firebase packages.\nWe need the Core package and since we want to use Cloud Firestore, we need that package, too.\nStep 4: Add Firebase initialization code.\nAdd await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); to main function and make it asynchronous.\nStep 5: Build a simple UI.\nI’ll go with one TextField widget, one ElevatedButton widget, and one Text widget inside of a StreamBuilder for the result.\nStep 6: Connect UI to Cloud Firestore.\nThe button sends the text to Firestore and the UI listens for changes. Here’s some code:\nAnd this is what the final example can look like:\nScreenshot of the final workflow with a Flutter app running in the browser to shorten a URL\nDemo of the final workflow with a Flutter app running in the browser to shorten a URL\nAn URL shortening service with a companion app in under one hour. It’s just amazing what you can build today!\nHow much is it? As mentioned earlier, this workflow is not for free. You will be charged for the following reasons:\n▶ Firebase resources of the extension (around 1 cent per month)\n▶ Cloud Firestore\n▶ Cloud Functions\n▶ Additional Firebase and Google Cloud Platform services\nSome of these services have free quotas (Cloud Firestore for example) but not all of them. The final price is always connected to the number of users your app will have. Monitoring your expenses is highly recommended.\nIf you are just experimenting with Firebase, you won’t get charged more than some cents per month.\nConclusion This is a small example of what you can build with Firebase and its features. The source code of the Flutter app is available on GitHub.\n","permalink":"https://quickcoder.org/posts/firebase-url-shortener/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eSave coding time by using Firebase Extensions and create an URL shortening service in minutes! Here’s the full guide.\u003c/p\u003e\n\u003cp\u003eWith a few simple steps, you can build your own personal URL shortening service. All you need is a \u003ca href=\"https://firebase.google.com\"\u003eFirebase account\u003c/a\u003e and a \u003ca href=\"https://bitly.com\"\u003eBitly account\u003c/a\u003e. The setup involves no coding. To test the service, we will write a small Flutter app.\u003c/p\u003e\n\u003cp\u003eFirebase offers a variety of extensions. The major benefit is that you gain functionality without having to code anything yourself. I already wrote a detailed \u003ca href=\"/posts/firebase-extensions/\"\u003earticle\u003c/a\u003e about it if you need more details.\u003c/p\u003e","title":"Create Your Own Custom URL Shortener with Firebase in Just a Few Simple Steps"},{"content":"Introduction Here is how you can implement the swipe-to-dismiss pattern in ListViews of a Flutter applications. It takes 5 minutes to implement it.\nSwipe to delete is a common UI pattern in mobile applications. The user swipes an element in a direction and the system reacts with an action. Common use cases include removing entries from lists or making toast notifications disappear, for example.\nIn this short article, I will show you how to implement the pattern in a ListView of a Flutter application.\nHere is the entire source code of this example:\nThe important part of our page is the ListView. Its items are wrapped in a Dismissible widget that handles all logic needed. Every entry needs a unique key so that it can be dismissed properly.\nHere are some properties that the Dismissible widget offers:\nonDismissed - what will happen when an item is dismissed confirmDismiss - happens just before onDismissed occurs. You can use it for a confirmation dialog, for example. direction - the allowed directions the user can swipe to background - in case you want to style the background of the swiped element And here is a short demo of how this can look like:\nDemo application using swipe to delete in a ListView of a Flutter application\nConclusion The swipe-to-dismiss pattern is easy to implement and one of the most basic pattern in mobile applications. With this guide, you should be able to implement it in your apps.\n","permalink":"https://quickcoder.org/posts/flutter-swipe-dismiss/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere is how you can implement the swipe-to-dismiss pattern in ListViews of a Flutter applications. It takes 5 minutes to implement it.\u003c/p\u003e\n\u003cp\u003eSwipe to delete is a common UI pattern in mobile applications. The user swipes an element in a direction and the system reacts with an action. Common use cases include removing entries from lists or making toast notifications disappear, for example.\u003c/p\u003e\n\u003cp\u003eIn this short article, I will show you how to implement the pattern in a \u003ccode\u003eListView\u003c/code\u003e of a Flutter application.\u003c/p\u003e","title":"How To Add Swipe To Dismiss In Flutter Apps For A Better UX"},{"content":"Introduction Here is how you add and use linters in your Flutter app to enhance and maintain higher code quality. Customize them to your needs!\nWith linters and linting rules, you can verify clean code principles and maintain high code quality during the development process of your Flutter app. A linter is a tool that analyzes source code to detect and report potential errors or issues.\nWhen you use a code editor like VSCode or Android Studio and you build your project, a static code analysis is executed to ensure, the code is free of compile errors. It uses the dart analyze command internally. On top of that, you can include additional rules with custom linters.\nHere is how you set them up in a Flutter project.\n🔔 Hint\nUsually, you should be fine with the default settings of a Flutter project.\nSetup To add a package of linter rules like flutter_lints, execute the command flutter pub add flutter_lints --dev. In your pubspec.yaml you’ll notice a new entry in the section dev_dependencies.\nOpen your analysis_options.yaml file (create an empty file if you don’t have it) and include the package with include: package:flutter_lints/flutter.yaml.\nYou can also copy the content of the analysis file of the package into your analysis_options.yaml file if you want to manage everything yourself.\nTo disable specific rules, open analysis_options.yaml, add a line with the rule name in the rules section, and give it the keyword false. Make sure to use the correct indentation!\nDisable a specific linter rule for the analyzer in the analysis_options.yaml file of a Flutter project\nTo enable specific rules that are disabled by default, add a new line and use the keyword true after the rule name. Here is a small demo video of how Visual Studio Code immediately handles changes of linter rules.\nVisual Studio Code immediate analysis update after changing a linter rule setting\nTo learn more about linter rules, check the official documentation.\nPopular linters ➡ flutter_lints\nThe default ruleset shipped with every Flutter app created after version 2.3. It contains 13 rules. It’s suited best for Flutter apps. In most cases, you will be fine with these rules.\n➡ lints\nOfficial linting recommendations by the Flutter and Dart team. It has 2 sets of linters: Core (around 30 rules) and Recommended (around 55 rules).\n➡ extra_pedantic\nAn alternative to lints with around 200 rules but nearly half of them are deactivated by default. If you want to dive deep into linting without writing your own rules, you can experiment with these to get going.\nConclusion In this article, we learned that linters are tools that can help developers write better code by detecting and reporting potential issues or errors. In addition, you should now be able to add linter packages and manage rules in your Flutter project.\n","permalink":"https://quickcoder.org/posts/flutter-linters/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eHere is how you add and use linters in your Flutter app to enhance and maintain higher code quality. Customize them to your needs!\u003c/p\u003e\n\u003cp\u003eWith linters and linting rules, you can verify clean code principles and maintain high code quality during the development process of your Flutter app. A linter is a tool that analyzes source code to detect and report potential errors or issues.\u003c/p\u003e\n\u003cp\u003eWhen you use a code editor like VSCode or Android Studio and you build your project, a static code analysis is executed to ensure, the code is free of compile errors. It uses the \u003ca href=\"https://dart.dev/tools/dart-analyze\"\u003edart analyze\u003c/a\u003e command internally. On top of that, you can include additional rules with custom linters.\u003c/p\u003e","title":"Clean Code, Happy Developer: How To Use Linters In Your Flutter App Development Workflow"},{"content":"Introduction With Firebase Extensions you can build a simple text translation app that does most of the work for you.\nThere are many ways to build a translation app. In this article, I am going to show you how to use Firebase for that task. A Flutter app will transmit the text to be translated to Firebase and display the final result. We will use Firebase Cloud Firestore and a Firebase Extension called Translate Text for this purpose.\n🔔 Hint\nBe aware that you might run into some costs. An installed extension costs around 1 cent per month and it normally uses other services in the background which can increase these costs. You will also need to activate the Blaze plan.\nFirebase setup First, we need to install the required extension. Go to Build → Extensions.\nScreenshot of Firebase Extensions menu entry by author\nAfterward, find the extension Translate Text and install it.\nScreenshot of Firebase Extension Translate Text by author\nFollow the wizard. During the process, you might encounter hints for additional services that need to be enabled for the extension to work.\nScreenshot of missing additional services to be enabled in Firebase by author\nConfigure the extension to your needs or use the default values. A guide on how to select the best location can be found here.\nScreenshot of extension configuration in Firebase by author\nThe process will take some minutes to complete.\nScreenshot of extension installing process in Firebase by author\nYou can see the details of the extension in the Extensions dashboard in Firebase. There is also an explanation of how to set up Cloud Firestore.\nScreenshot of installed extension in Firebase by author\nNow, we switch to the Cloud Firestore database. According to the instructions we create a new collection called translations and a document with a field called input. The value of the input field will be translated to the configured languages and stored in the translated field.\n🔔 Hint\nWhat happens behind the scenes? When the field value changes, a cloud function is triggered (you can see it in the Cloud Functions Dashboard of Firebase). It uses the Cloud Translation API to translate the text and writes the result back to the Cloud Firestore document.\nThe Firebase setup is completed. Let’s write a simple Flutter app to make use of our workflow.\nApp setup We need to create an app that writes text to Cloud Firestore and listens to changes which it will then display to the user.\nStep 1: Create a Flutter project.\nUse the command flutter create my_sample_project or your IDE of choice for it.\nStep 2: Link it with your Firebase project.\nRun flutterfire configure from the project root folder and follow the wizard.\nStep 3: Install Firebase packages.\nWe need the Core package and since we want to use Cloud Firestore, we need that package, too.\nStep 4: Add Firebase initialization code.\nAdd\nawait Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); to main function and make it asynchronous.\nStep 5: Build a simple UI.\nI’ll go with one TextField widget, one ElevatedButton widget, and 4 Text widgets inside of a StreamBuilder for the translations.\nStep 6: Connect UI to Cloud Firestore.\nThe button sends the text to Firestore and the UI listens for changes. Here’s some code:\nAnd this is what the final example can look like:\nVideo of the demo translation app by author\nIt will take you an hour at most to get to this result. A translation feature that can be integrated in any app.\nHow much is it? As mentioned earlier, this workflow is not for free. You will be charged for the following reasons:\n▶ Firebase resources of the extension (around 1 cent per month)\n▶ Cloud Firestore\n▶ Cloud Functions\n▶ Cloud Translation API\n▶ Additional Firebase and Google Cloud Platform services\nSome of these services have free quotas (Cloud Firestore for example) but not all of them. The final price is always connected to the number of users your app will have. Monitoring your expenses is highly recommended.\nIf you are just experimenting with Firebase, you won’t get charged more than some cents per month.\nConclusion This is a small example of what you can build with Firebase and its features. The source code of the Flutter app is available on GitHub.\n","permalink":"https://quickcoder.org/posts/firebase-translation/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eWith Firebase Extensions you can build a simple text translation app that does most of the work for you.\u003c/p\u003e\n\u003cp\u003eThere are many ways to build a translation app. In this article, I am going to show you how to use Firebase for that task. A Flutter app will transmit the text to be translated to Firebase and display the final result. We will use \u003cstrong\u003eFirebase Cloud Firestore\u003c/strong\u003e and a \u003cstrong\u003eFirebase Extension\u003c/strong\u003e called \u003cstrong\u003eTranslate Text\u003c/strong\u003e for this purpose.\u003c/p\u003e","title":"Building A Simple Text Translation App With Flutter And Firebase"},{"content":"Introduction Flutter offers predefined widgets for dialogs, toasts, and overlays. This article shows how you use them properly with code examples. Here is how to work with dialogs, toasts, and overlays in Flutter apps!\nDialogs are a common pattern in many applications. In this article, I’ll introduce you to the built-in possibilities of Flutter for creating dialogs. We will cover the following widget types:\n▶ AlertDialog (and CupertinoAlertDialog)\n▶ SimpleDialog\n▶ Dialog\n▶ Overlay\n▶ Toast\nAlertDialog (and CupertinoAlertDialog) The AlertDialog is the most simple way to display information to the user that he needs to react to. It’s a simple widget with a title (heading), a content (message), and a list of actions (one or more buttons).\nThe AlertDialog has some additional properties compared to the CupertinoAlertDialog. There is, for example, the icon property that lets you add an icon on top of the heading.\nTo invoke the widget, use the showDialog function. If you don’t want that users can close the dialog by tapping anywhere outside of it, set the barrierDismissible value to false. In this case, you must take care of closing the dialog with Navigator.of(context).pop(), for example.\nHere are two screenshots from the demo application of the dialogs:\nFlutter AlertDialog example by author\nFlutter CupertinoAlertDialog example by author\nSimpleDialog The SimpleDialog gives you more freedom to design your dialog. The basic properties are only a title and a list of children. Its main purpose is to choose from a selection of options. Instead of a button bar at the bottom, you can add multiple selection options with the SimpleDialogOption widget. Like the AlertDialog, the SimpleDialog is triggered by the showDialog() method.\nFlutter SimpleDialog example by author\nDialog The Dialog is the purest dialog you can have. It needs only one child, so you can use your entire creativity to style it the way you want. Apart from that, it works similarly to the AlertDialog and the SimpleDialog.\nFlutter Dialog example by author\nOverlay An Overlay is a widget that will be placed on top of all other widgets. You can think of it as a Stack widget, but it can be put on top of any page in your app. You need to create an OverlayEntry first that contains the widget to be displayed. To display it, call Overlay.of(context)!.insert(overlayEntry);. To remove it, call overlayEntry?.remove();.\nOverlays don’t use the ThemeData of Text widgets. So you need to style all Text widgets or your Overlay will look like this:\nFlutter Overlay example by author\nFlutter Overlay example by author\nToast To display a toast notification, a SnackBar can be used. A SnackBar will be displayed at the bottom of your screen and will auto-hide after 4 seconds by default. You can add any widgets you want and customize various properties like dismissDirection (a swipe to the set direction will hide the widget) or duration (timespan after that the widget hides).\nA SnackBar can only be used inside a Scaffold. To show a SnackBar, call the showSnackBar(…) method of the ScaffoldMessengerState. Hiding works similarly with the hideSnackBar() method.\nFlutter SnackBar example by author\nConclusion In this article, you saw the differences between the different dialog widgets and how to use them. You can find the demo application with the working examples on GitHub.\n","permalink":"https://quickcoder.org/posts/flutter-dialogs/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eFlutter offers predefined widgets for dialogs, toasts, and overlays. This article shows how you use them properly with code examples. Here is how to work with dialogs, toasts, and overlays in Flutter apps!\u003c/p\u003e\n\u003cp\u003eDialogs are a common pattern in many applications. In this article, I’ll introduce you to the built-in possibilities of Flutter for creating dialogs. We will cover the following widget types:\u003c/p\u003e\n\u003cp\u003e▶ \u003ca href=\"https://api.flutter.dev/flutter/material/AlertDialog-class.html\"\u003eAlertDialog\u003c/a\u003e (and \u003ca href=\"https://api.flutter.dev/flutter/cupertino/CupertinoAlertDialog-class.html\"\u003eCupertinoAlertDialog\u003c/a\u003e)\u003cbr\u003e\n▶ \u003ca href=\"https://api.flutter.dev/flutter/material/SimpleDialog-class.html\"\u003eSimpleDialog\u003c/a\u003e\u003cbr\u003e\n▶ \u003ca href=\"https://api.flutter.dev/flutter/material/Dialog-class.html\"\u003eDialog\u003c/a\u003e\u003cbr\u003e\n▶ \u003ca href=\"https://api.flutter.dev/flutter/widgets/Overlay-class.html\"\u003eOverlay\u003c/a\u003e\u003cbr\u003e\n▶ \u003ca href=\"https://api.flutter.dev/flutter/material/SnackBar-class.html\"\u003eToast\u003c/a\u003e\u003c/p\u003e","title":"How To Work With Dialogs, Toasts, And Overlays In Flutter Apps"},{"content":"Introduction Instead of writing code yourself, you can always use generators to create Dart model classes from JSON. Here are the results of my evaluation of some free tools for you.\nFor this test, I created a sample JSON structure that all tools need to deal with. You can find it below:\nEvery converter will receive the same input and I’ll examine the created output. Is it runnable? Is null-safety supported? Is the code efficient? Is anything missing? Have a look at the results below.\nI started an internet search and took the first results I found. The list of participants are\nQuicktype JSON to Dart Converter JSON formatter JSON to Dart Model Dart QuickType JSON to Dart online converter Null Safety TLDR Go with Dart QuickType. It generates the best Dart code from JSON objects.\nQuicktype Quicktype is a multi-language converter for JSON objects and it also supports Dart. There are options to customize the generated class. And there is also an extension for Visual Studio Code.\nThe result contains everything, but there are problems:\nQuicktype doesn’t use the required keyword, but the old @required annotation Quicktype doesn’t support null-safety This makes the code unusable without manual changes. There are also 77 open issues for the Dart language and the official source code hasn’t been updated for months. I wouldn’t expect any progress here, but the tool still saves development time despite the necessary manual changes.\nSolid, but not perfect. Faster than writing by yourself.\nJSON to Dart Converter JSON to Dart Converter is a free online Dart model class generator. The only available option is to make fields private. In that case, getters and setters are generated.\nThe JSON can be converted into a Dart class but with major problems:\nNo null-safety Getters and setters are generated for fields when they are private Array was not recognized as List\u0026lt;int\u0026gt;, but as Object\u0026lt;int\u0026gt; List of type Object was not recognized Overall the code is pretty much useless because you’ll need so much time fixing the errors that you are faster if you write it by yourself. There is no GitHub repository or anything similar, just a mail address for contact. The website looks like it started yesterday and there is no content except this converter.\nDon’t use it, it’s a waste of time! ⛔\nJSON formatter JSON formatter is another free online Dart model class generator. There are no options to select, you just input the JSON and the tool converts it into a Dart class.\nThe output is useless, there aren’t even fromJson and toJson methods included. No null-safety, no final fields, but at least the object structure was converted correctly.\nAbsolutely not worth it! ⛔\nJSON to Dart Model JSON to Dart Model provides many customization options for the output. The code is open-source and there are currently 3 open issues. The project doesn’t seem to be as known as quicktype, but the generated code will work out of the box with the current Dart version.\nIt’s a bit annoying that the converted JSON snippets are included in the code as comments. Also, the conversion of arrays seems very cumbersome. And sadly, the converter created two identical objects with different names. But apart from that, it looks good. However, I wouldn’t use the result as the code style is very odd and I’d have to do a lot of refactoring.\nWorking and usable, but odd code style.\nDart QuickType Dart QuickType is a fork of Quicktype with a much more recent code base. All of the mentioned flaws of Quicktype were fixed here. There are no open issues and the created Dart code looks simple and slim.\nDefault values, null-safety, final fields … everything you need! The only little thing that I would like to have added is equatable because I use it all the time. But I can tolerate this one manual step. Maybe the author works on my proposal for a future release.\nUpdate: equatable was added in a recent release!\nClear winner, no objections, best code generation! ✔\nJSON to Dart online converter Null Safety JSON to Dart online converter Null Safety has no options except for setting the class name and the generated code does not work out of the box.\nSome flaws with this code:\nUnnecessary late keyword when using required Variables can be named like keywords → compiler error Creates two identical classes Object and List No fallback values in the fromJson method Additional compiler errors with created classes Too many manual corrections needed to be useful. ⛔\nConclusion Dart QuickType generates the best code and is the winner of this comparison. To some extent, you can also use JSON to Dart Model if you don’t mind the code style. All other converters are not worth the time, so just skip them.\n","permalink":"https://quickcoder.org/posts/dart-json-tools/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eInstead of writing code yourself, you can always use generators to create Dart model classes from JSON. Here are the results of my evaluation of some free tools for you.\u003c/p\u003e\n\u003cp\u003eFor this test, I created a sample JSON structure that all tools need to deal with. You can find it below:\u003c/p\u003e\n\u003cscript src=\"https://gist.github.com/xeladu/533edbb91b883df86aff8cb168c0ef39.js\"\u003e\u003c/script\u003e\n\u003cp\u003eEvery converter will receive the same input and I’ll examine the created output. Is it runnable? Is null-safety supported? Is the code efficient? Is anything missing? Have a look at the results below.\u003c/p\u003e","title":"A Comparison Of 6 JsonToDart Tools For Flutter/Dart Developers"},{"content":"Introduction A new Flutter project contains a lot of files and folders. In this article, we’ll inspect them and reveal their purposes.\nLet’s start with an overview of what a standard Flutter project with version 3.0 will contain. We will have a look at each item in the list and discuss its purpose.\nScreenshot of default Flutter project structure by author\n.dart_tool folder Screenshot of .dart_tool folder by author\nThis folder contains files used by various Dart tools.\nandroid folder Screenshot of android folder by author\nThis folder contains all relevant data to compile and create a working Android app (apk or appbundle). There are two files that you most likely will need to edit during the development process:\n➡️ build.gradle in android/app\nIt contains the applicationId that is required when submitting to Google Play Store. Make sure, this value matches the value you specify in Google Play Console before uploading your app.\n❗Warning\nBe sure to check the path! There is another build.gradle file in the android subfolder.\n➡️ AndroidManifest.xml in android/app/src/main\nHere you can specify intents or capabilities that your app is going to use. When using 3rd party packages, their installation instructions usually include detailed information on what needs to be added here.\n❗Warning\nAgain be sure to check the path because there are other manifest files at android/app/src/debug and android/app/src/profile!\nbuild folder The build folder will be created when the Flutter build tools are executed for the first time. It contains the generated files needed to run the app on the different platforms. Each platform has its own subfolder.\nios folder Screenshot of ios folder by author\nSimilar to the android folder but for ios-related files. The files you are most likely going to edit are:\nAppDelegate.swift in ios/Runner\nThe entry point for the ios app. 3rd party packages might need to have some initialization logic added here. Check their documentation for details.\nInfo.plist in ios/Runner\nContains all app-related settings. Additional features and capabilities might require a new entry in this file.\nlib folder Screenshot of lib folder by author\nThe lib folder is where your app code will be located. Make sure to plan your code structure before starting to develop your app. Otherwise, it can get confusing quickly without proper organization.\ntest folder Screenshot of test folder by author\nPut all your test files here so that the framework can find them when, for example, the flutter --test command is executed. Every file also needs a suffix _test.dart to be successfully found. Stick to that pattern and you’ll have no problems. It is considered good practice when your test folder structure matches your lib folder structure. This will speed up navigation inside the project for developers.\nweb folder Screenshot of web folder by author\nThe web-specific code can be found in this folder. You might want to change icons or add 3rd party package in index.html. Again, have a look at the documentation.\nwindows/linux/macos folder Screenshot of windows folder by author\nEverything for your Windows/Linux/MacOS app will be placed in this folder. It will be created depending on the platform, you are currently using. So you will always have only one of the folders. In the example screenshot above, it’s a Windows system with a Windows folder.\n.gitignore file Contains information about what files and folders are to be excluded from version control. Here you might want to add any API keys your app is going to use so that they are not exposed in a public repository.\n.metadata file The file is controlled by the Flutter tool and should not be edited manually.\n.packages file Deprecated file, contains information about used packages in the project. Do not edit manually! Can be removed after Dart 2.17 according to the official documentation. Not sure, what Dart version you are using? Run the command dart --version or flutter doctor -v.\nanalysis_options.yaml file The ruleset for the dart code analyzer. To configure the analyzer, have a look at the official documentation.\ndummy.iml This file is managed by the Flutter SDK and you should not edit it manually. The name always matches your project name during the initial generation.\npubspec.lock/pubspec.yaml The pubspec contains project descriptions, constraints, dependencies, and assets. All your 3rd party packages will be defined here so that the Flutter tools know what version should be fetched. You can find more information about the pubspec.yaml in this article.\nThis is your project presentation that is used by most source code providers as an informational page.\nConclusion If you ever wondered what all these files and folders in a Flutter application are there for, this article answered probably most of your questions. But if there are still open points to discuss, feel free to leave a comment and I’ll come back to you for sure.\n","permalink":"https://quickcoder.org/posts/flutter-project-structure/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eA new Flutter project contains a lot of files and folders. In this article, we’ll inspect them and reveal their purposes.\u003c/p\u003e\n\u003cp\u003eLet’s start with an overview of what a standard Flutter project with version 3.0 will contain. We will have a look at each item in the list and discuss its purpose.\u003c/p\u003e\n\u003cfigure\u003e\n\u003cp\u003e\u003cimg alt=\"Screenshot of default Flutter project structure by author\" loading=\"lazy\" src=\"/posts/flutter-project-structure/images/folders.png\"\u003e\u003c/p\u003e\n\u003cfigcaption\u003e\n\u003cp\u003eScreenshot of default Flutter project structure by author\u003c/p\u003e\n\u003c/figcaption\u003e\n\u003c/figure\u003e\n\u003ch1 id=\"dart_tool-folder\"\u003e.dart_tool folder\u003c/h1\u003e\n\u003cfigure\u003e\n\u003cp\u003e\u003cimg alt=\"Screenshot of .dart_tool folder by author\" loading=\"lazy\" src=\"/posts/flutter-project-structure/images/dart_tool_folder.png\"\u003e\u003c/p\u003e","title":"All You Need To Know About The Default Flutter Project Folders And Files"},{"content":"Introduction The pubspec.yaml file is the main configuration file for your Flutter app. In this article, we’ll have a look at its contents.\nWhen developing a Flutter app or package, you’ll eventually come across the pubspec.yaml file. I’ll give you an overview of the contents and how to work with the file. It contains package dependency information, SDK version constraints, or project settings like the name. The used syntax is YAML, an easy-to-use and understandable format. Pay attention to indentation, colons, and link breaks. Otherwise, there will be parsing errors by the Flutter/Dart tooling.\nThe default file When creating a new Flutter project, a pubspec.yaml file is automatically created depending on the project template. The app template’s file that will be created if you don’t specify another template looks like this (comments were removed):\n▶ name\nThe name of the app. Allowed characters are lower-case letters, numbers, and underscores.\n▶ description\nA short description of your app.\n▶ publish_to\nAlways none for apps.\n▶ version\nYour app version with semantic versioning syntax.\n▶ environment\nDart (sdk) and Flutter (flutter) SDK version constraints. Always specify in ^x.x.x notation.\n▶ dependencies\nList of all packages that your app needs to work\n▶▶ flutter\nSection is required for every Flutter project\n▶▶▶ sdk\nSection is required for every Flutter project. Value must be flutter\n▶▶ cupertino_icons\nRequired when using iOS style icons\n▶ dev_dependencies\nList of all packages that your app needs during development to work (for example packages for unit testing)\n▶▶ flutter_test\nSection is required for every Flutter project that includes tests\n▶▶▶ sdk\nSection is required for every Flutter project that includes tests. Value must be flutter\n▶ flutter\nAll Flutter related settings can be found in this section\n▶▶ uses-material-design\nRequired if you use the Material icon font\n▶▶ assets\nAny file you want to include in your app (like TXT, JSON, PNG, BMP) comes here. Either specify the correct relative path to a file (- images/a_dot_burr.jpeg) or use a folder path with a wildcard to include all files of that folder (- images/). For more information about assets have a look at the following link\nAdding assets and images ▶▶ fonts\nRequired if your app uses custom fonts. The following link provides additional details:\nUse a custom font Notes The file must be located in the project root folder The file name must not be changed If the YAML syntax is messed up, the Dart/Flutter tooling will indicate where the error is On the first build, a pubspec.lock file will be generated from the pubspec.yaml file 💡 Tip\nIf you want to develop a package and publish it to the Dart package repository, additional fields will be required. Have a look at this link for more details.\nConclusion This brief summary should give you an idea of what the pubspec.yaml file is all about. If you need more information, check the Flutter docs.\n","permalink":"https://quickcoder.org/posts/flutter-pubspec/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eThe \u003ccode\u003epubspec.yaml\u003c/code\u003e file is the main configuration file for your Flutter app. In this article, we’ll have a look at its contents.\u003c/p\u003e\n\u003cp\u003eWhen developing a Flutter app or package, you’ll eventually come across the \u003ccode\u003epubspec.yaml\u003c/code\u003e file. I’ll give you an overview of the contents and how to work with the file. It contains package dependency information, SDK version constraints, or project settings like the name. The used syntax is \u003ca href=\"https://yaml.org/\"\u003eYAML\u003c/a\u003e, an easy-to-use and understandable format. Pay attention to indentation, colons, and link breaks. Otherwise, there will be parsing errors by the Flutter/Dart tooling.\u003c/p\u003e","title":"The Flutter pubspec.yaml In Detail"},{"content":"Introduction If you are not feeling well, you go and see a doctor. The same goes for Flutter.\nThe Flutter diagnostics tool helps you identify configuration problems that might hinder a successful development workflow. As the output can be very helpful, it is required to include a diagnostic summary in every bug report in the official Flutter GitHub repository. Let’s have a look at the tool and its results.\nCalling the doctor Run the command flutter doctor from your preferred command-line tool. Your output might look similar to this one:\nScreenshot of diagnostics output by author\nThe tool lists information about\nFlutter and OS version Possible dependency problems with the Android toolchain Development tools like Chrome, Visual Studio, and Android Studio Connected devices that you can deploy your app to As I am on a Windows machine right now, there is no information about any Linux, iOS, or macOS errors because I can’t create these app types from a Windows OS. So if you are on Linux or macOS, your output might look different.\nIn case of errors, you’ll typically get a detailed error message that points you towards a solution. Common problems involve Android toolchain errors, Android license errors, or no connected devices.\nCalling the chatty doctor Run the command flutter doctor -v from your preferred command-line tool. Your output might look similar to this one:\nScreenshot of diagnostics output by author\nCompared to the previous output, we receive a lot more information and details for every group. The command tends to run twice as long as the other one to gather all the additional facts. SDK paths, install locations, plugin and tool version information, recognized devices including deployment targets and version info, and much more.\nThe doctor came, shoveled a lot of information upon you, and then left again. If you don’t see an error here, if you spot all your devices (browsers, phones, emulators, desktop platforms), and if all your IDEs are found, you’ll be fine.\nConclusion Flutter doctor is the first source for solving problems when you are experiencing strange behavior and error messages. It is part of the initial Flutter setup and will also be executed on every version upgrade. With this article, you might understand the details better.\n","permalink":"https://quickcoder.org/posts/flutter-doctor/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eIf you are not feeling well, you go and see a doctor. The same goes for Flutter.\u003c/p\u003e\n\u003cp\u003eThe Flutter diagnostics tool helps you identify configuration problems that might hinder a successful development workflow. As the output can be very helpful, it is required to include a diagnostic summary in every bug report in the \u003ca href=\"https://github.com/flutter/flutter\"\u003eofficial Flutter GitHub repository\u003c/a\u003e. Let’s have a look at the tool and its results.\u003c/p\u003e\n\u003ch1 id=\"calling-the-doctor\"\u003eCalling the doctor\u003c/h1\u003e\n\u003cp\u003eRun the command \u003ccode\u003eflutter doctor\u003c/code\u003e from your preferred command-line tool. Your output might look similar to this one:\u003c/p\u003e","title":"How To Use The Flutter Doctor Diagnostics Tool In The Right Way"},{"content":"Introduction In this article, we are having a look at location services and how we can get the current position of the device in our Flutter application.\nLocation information can be used in various app scenarios. Maybe you want to show points of interest to the users or guide them towards a specific area. Either way, you will need access to the current location of the device. I’ll show you how to accomplish this task.\nSetup The first step is the installation of a package. There are many packages available at pub.dev, but I’ll demonstrate the steps with the most popular one: location. The setup process should be quite similar for all packages, the code however might be slightly different.\nWe also need to add capabilities to our app, so that the location request can be granted by the operating system and the user.\nFor Android, go to android/app/src/main/AndroidManifest.xml and add the capability\n\u0026lt;uses-permission android:name=\u0026#34;android.permission.ACCESS_FINE_LOCATION\u0026#34; /\u0026gt; or\n\u0026lt;uses-permission android:name=\u0026#34;android.permission.ACCESS_COARSE_LOCATION\u0026#34; /\u0026gt; to the file. The first one is more accurate but also requires more battery power. The entry should inside the \u0026lt;manifest\u0026gt; tag but not inside the \u0026lt;application\u0026gt; tag.\nFor iOS, go to ios/Runner/Info.plist and add the following code inside the \u0026lt;dict\u0026gt; node.\n\u0026lt;key\u0026gt;NSLocationWhenInUseUsageDescription\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; Code Here is a simple code example that initializes the plugin. You can call it for example in your initState() method of your main app state.\nThe following steps are performed:\nCheck if device location services are enabled Check if usage permission for location services was granted Output the current position to the debug console ❗Warning\nIf location services are disabled, you will be asked to activate them.\n❗Warning\nIf usage permission has not been granted yet, you will be asked on Android. iOS will always ask on app launches. If you deny the usage, you won’t be prompted again. To change the setting, you’ll have to go to your phone settings and enable the permission for this app.\nTo get continouos information about position changes, you can add a listener.\nlocation.onLocationChanged.listen((LocationData loc) { print(\u0026#34;${loc.latitude} ${loc.longitude}\u0026#34;); }); Conclusion In this article, I showed you how to set up and work with location data. The configuration can be a bit tricky at first, but the possibilities of location data are endless.\n","permalink":"https://quickcoder.org/posts/flutter-location/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eIn this article, we are having a look at location services and how we can get the current position of the device in our Flutter application.\u003c/p\u003e\n\u003cp\u003eLocation information can be used in various app scenarios. Maybe you want to show points of interest to the users or guide them towards a specific area. Either way, you will need access to the current location of the device. I’ll show you how to accomplish this task.\u003c/p\u003e","title":"How To Use Location Services In Your Flutter Application"},{"content":"Introduction In this article, we are looking at the Flutter CLI and its most common use cases for building, testing, deploying, and running Flutter applications.\nWhen developing Flutter apps with code editors like Visual Studio Code, Android Studio, or XCode, you don’t need to use the command-line tools of Flutter in general. But all these editors use them internally all the time, probably without you noticing it. So let’s dive into the Flutter CLI and see what its capabilities are.\nGeneral ▶ flutter doctor [-v]\nThe doctor tells you the current state of your system and if there is anything missing to work with Flutter. It shows SDK versions, finds code editors, lists connected devices, and identifies problems. Use the -v switch to get more detailed information.\nOutput of the Flutter doctor command\n▶ flutter create\nWith this command, you can create a new Flutter project or repair an existing one. It allows you to specify a project name (--project-name), a description (--description), supported platforms like iOS, Android, Windows, Linux, macOS, and web (all are enabled by default), or what type of project it is (--template).\nOutput of Flutter create command\n▶ flutter channel\nUse this to switch between the different development channels. There are currently four channels available: stable, beta, dev, and master. Stable is the safest channel with the most testing done, master contains hot new stuff and can change daily.\nOutput of Flutter channel command\n▶ flutter upgrade\nUpgrades to another version on the current channel. With the switch\n--verify-only you can check for new versions without upgrading.\nOutput of Flutter upgrade command\nImplementation ▶ flutter pub [add/outdated/upgrade/get]\nWith this command, you can manage your packages in your Flutter app. It helps you add packages with add, find outdated versions with outdated, upgrade them with upgrade, or synchronize them with get.\nOutput of Flutter pub outdated command\n▶ flutter build [target]\nThe build command creates a deployable app from your code to the target platform. You need to provide a subcommand to indicate what target you want to build. Your options are\nAndroid (apk,appbundle) ➡ How to build for Android iOS (ios,ipa) ➡ How to build for iOS Web (web) ➡ How to build for web Linux (linux) ➡ How to build for Linux Windows (windows) ➡ How to build for Windows Mac OS (macos) ➡ How to build for Mac OS Only available options are shown in the help menu (flutter build -h) depending on your current system. That means if you are, for example, developing on a Windows machine, you won’t have the option to build for Mac OS.\nOutput of Flutter build command\n▶ flutter clean\nThis command cleans caches and deletes build output data. If your app isn’t working as expected or throwing suspicious errors, try this command and run it again.\nOutput of Flutter clean command\nDeployment ▶ flutter test\nRuns unit tests of the current project. This command has many options available. You can measure code coverage with the switch --coverage for example.\nOutput of Flutter test command\n▶ flutter devices\nList all available and connected devices like mobiles, browsers, or operating systems.\nOutput of Flutter devices command\n▶ flutter install\nInstalls the app to a device. If multiple devices are available, the command will ask for the device.\nOutput of Flutter install command\n▶ flutter run [arguments]\nRuns the current app on the selected device. Here are some frequently used arguments:\n--debug runs in debug mode --profile runs in profile mode for optimized performance measuring --release runs in release mode --trace-startup trace app startup and save file into build directory --route define a custom route to load on app launch --flavor build a custom flavor/scheme depending on the platform Output of Flutter run command\nConclusion The Flutter CLI is very powerful and offers some very good and useful tools. But there might be a chance that you don’t ever need to run a single command yourself because your IDE does all the work for you. But if you ever need to use the CLI, those insights above might be quite handy.\n","permalink":"https://quickcoder.org/posts/flutter-cli/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eIn this article, we are looking at the Flutter CLI and its most common use cases for building, testing, deploying, and running Flutter applications.\u003c/p\u003e\n\u003cp\u003eWhen developing Flutter apps with code editors like Visual Studio Code, Android Studio, or XCode, you don’t need to use the command-line tools of Flutter in general. But all these editors use them internally all the time, probably without you noticing it. So let’s dive into the Flutter CLI and see what its capabilities are.\u003c/p\u003e","title":"How To Use The Flutter Command-Line Interface"},{"content":"Introduction In this article, I am going to present my top 5 free extensions for Visual Studio Code to you. They offer a jump start for your development progress.\nVS Code is a very good code editor, not only in my opinion (see StackOverflow developer survey 2021). But today’s tools can be enhanced with extensions to make them perfect for your specific tasks. Here are the extensions that I use as a Flutter developer.\nTodo Tree Todo Tree collects all comments starting with specific tags like bug, hack, fixme, or todo. They are organized into a custom view where they are grouped by folders and classes. It helps to keep track of code parts that need enhancements.\nThe tags to look for can be changed. The extension can also hook into the status bar and show the current finding count for the current file, for example.Many settings cann be customized.\nScreenshot of Todo Tree in VS Code\nGistPad GistPad is a useful extension, especially for writers. If you regularly use GitHub Gists to share code, GistPad is a must-have extension. You can connect it with your GitHub account and create, edit, delete, or share gists from the VS Code UI.\nScreenshot of GistPad in VS Code\nCoverage Gutters Coverage Gutters adds visual markers on code lines that have been covered by unit tests. It uses collected coverage information, e.g. from a lcov.info file. With this extension, you can spot untested code parts very easily.\nScreenshot of Coverage Gutters in VS Code\nFlutter Coverage Flutter Coverage adds a panel to the test tab in VS Code to show the covered lines per class in percent. It helps you find classes that need more testing. The thresholds are customizable.\nScreenshot of Flutter Coverage in VS Code\nAwesome Flutter Snippets Awesome Flutter Snippets reduces typing by adding shortcuts to create boilerplate code. When entering these shortcuts, they get replaced with the full-blown implementation code. It’s very useful when you have a lot of widgets like ListViews or GridViews.\nScreenshot of Awesome Flutter Snippets in VS Code\nConclusion These are my must-have extensions when developing Flutter applications. They boost productivity and help you get things done quicker.\n","permalink":"https://quickcoder.org/posts/flutter-vscode-extensions/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eIn this article, I am going to present my top 5 free extensions for Visual Studio Code to you. They offer a jump start for your development progress.\u003c/p\u003e\n\u003cp\u003eVS Code is a very good code editor, not only in my opinion (\u003ca href=\"https://insights.stackoverflow.com/survey/2021#most-loved-dreaded-and-wanted-new-collab-tools-love-dread\"\u003esee StackOverflow developer survey 2021\u003c/a\u003e). But today’s tools can be enhanced with extensions to make them perfect for your specific tasks. Here are the extensions that I use as a Flutter developer.\u003c/p\u003e","title":"Here Are My Top 5 Free extensions For VS Code For Flutter Developers"},{"content":"Introduction. Problems when merging or rebasing branches in a version control system like git can happen to anyone. I want to show you one way that works pretty good to resolve the issue. Of course, there are other ways like reverting the rebase and doing it again, but I like this approach more.\nRelevant git commands ▶ git cherry-pick\n▶ git log\n▶ git checkout\n▶ git branch\n1 Create a new branch from the merge/rebase target Let’s assume you wanted to rebase the main branch onto your dev branch and something went wrong because the application does launch anymore.\nSo you create a new branch dev2 from the main branch as the first step. That means you have all the latest changes from main already in your branch and you don’t need to rebase anymore.\n2 Apply the changes from the broken branch to the new branch The next step is to apply all your changes from dev to dev2. Switch to your dev2 branch and get all commits with the git cherry-pick \u0026lt;id\u0026gt; command.\nTo find the commit ids, you can use the git log command on the dev branch (see image below). Apply the changes in chronological order. In the end, you have a branch with all of your changes and also the changes of the rebase source.\nGit log with commit ids\n3 Push the new branch Finally, push the dev2 branch. You might need to set the upstream, but git will assist you with that.\n4 Delete the broken branch The original dev branch is useless now. You can delete it in your local repository as well as in the remote repository.\nConclusion I showed you a strategy to fix problems with a broken branch by replacing it with a working one. It’s a simple and straightforward approach and might help you in the future.\n","permalink":"https://quickcoder.org/posts/git-rebase-fix/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction.\u003c/h1\u003e\n\u003cp\u003eProblems when merging or rebasing branches in a version control system like git can happen to anyone. I want to show you one way that works pretty good to resolve the issue. Of course, there are other ways like reverting the rebase and doing it again, but I like this approach more.\u003c/p\u003e\n\u003ch2 id=\"relevant-gitcommands\"\u003eRelevant git commands\u003c/h2\u003e\n\u003cp\u003e▶ \u003ca href=\"https://git-scm.com/docs/git-cherry-pick\"\u003egit cherry-pick\u003c/a\u003e\u003cbr\u003e\n▶ \u003ca href=\"https://git-scm.com/docs/git-log\"\u003egit log\u003c/a\u003e\u003cbr\u003e\n▶ \u003ca href=\"https://git-scm.com/docs/git-checkout\"\u003egit checkout\u003c/a\u003e\u003cbr\u003e\n▶ \u003ca href=\"https://git-scm.com/docs/git-branch\"\u003egit branch\u003c/a\u003e\u003c/p\u003e\n\u003ch1 id=\"1-create-a-new-branch-from-the-mergerebase-target\"\u003e1 Create a new branch from the merge/rebase target\u003c/h1\u003e\n\u003cp\u003eLet’s assume you wanted to rebase the \u003ccode\u003emain\u003c/code\u003e branch onto your \u003ccode\u003edev\u003c/code\u003e branch and something went wrong because the application does launch anymore.\u003c/p\u003e","title":"How To Fix A Broken Branch After A Git Merge Or Git Rebase"},{"content":"Introduction This article is all about the possibilities of mocking that the Mockito package provides. We’ll look into setting up mocks, verifying the calls, and pitfalls to avoid.\nI recently published an article about mocking in unit tests with the Mockito package in Flutter apps.\nThis follow-up article is a cheat sheet for you to quickly find an example of how to use the package. I also updated the GitHub repository of the original post and it now contains the cheat sheet as a working test class.\nFinal tips ✅ Always use thenAnswer(), never use thenReturn() ▶ Explanation\n✅ If you verify a mock, the invocation counter is reset!\n✅ If there are multiple matching setups, the last setup wins!\nSource code You can find the source code on GitHub.\n","permalink":"https://quickcoder.org/posts/mockito-cheat-sheet/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eThis article is all about the possibilities of mocking that the Mockito package provides. We’ll look into setting up mocks, verifying the calls, and pitfalls to avoid.\u003c/p\u003e\n\u003cp\u003eI recently published an article about mocking in unit tests with the \u003ca href=\"https://pub.dev/packages/mockito\"\u003eMockito package\u003c/a\u003e in Flutter apps.\u003c/p\u003e\n\u003cp\u003eThis follow-up article is a cheat sheet for you to quickly find an example of how to use the package. I also updated the GitHub repository of the original post and it now contains the cheat sheet as a working test class.\u003c/p\u003e","title":"Flutter Mockito Package Cheat Sheet"},{"content":"My name is Alex, I’m from Germany and I am a passionate software developer for many many years. That’s why I’d like to bring my enthusiasm to other people who are interested in coding. In my repos you can find many code examples for my blog posts.\nYou can always reach out to me via email.\n▶ I\u0026rsquo;m Alex from Germany 💻 Passionate software developer for many years ❤ Enthusiastic and passionate about coding 📝 I regularly write articles on https://xeladu.medium.com ✍ All my tutorials are also available on QuickCoder ","permalink":"https://quickcoder.org/about/","summary":"\u003cp\u003eMy name is Alex, I’m from Germany and I am a passionate software developer for many many years. That’s why I’d like to bring my enthusiasm to other people who are interested in coding. In my repos you can find many code examples for my blog posts.\u003c/p\u003e\n\u003cp\u003eYou can always reach out to me via \u003ca href=\"mailto:xeladu@quickcoder.org\"\u003eemail\u003c/a\u003e.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e▶ I\u0026rsquo;m Alex from Germany\u003c/li\u003e\n\u003cli\u003e💻 Passionate software developer for many years\u003c/li\u003e\n\u003cli\u003e❤ Enthusiastic and passionate about coding\u003c/li\u003e\n\u003cli\u003e📝 I regularly write articles on \u003ca href=\"https://xeladu.medium.com\"\u003ehttps://xeladu.medium.com\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e✍ All my tutorials are also available on QuickCoder\u003c/li\u003e\n\u003c/ul\u003e","title":"About Me"}]