Photo by Goh Rhy Yan on Unsplash

Components of an App

Let’s take a look at what are the components or things we must handle in a Real-World App

Navigation Networking Theming and Styling Organizing Code Extras: Tools, Docs and Assets

Navigation -

1. Defining the Routes

I t’s best to use Named Routes only, as this helps you to always be aware of the Screens and Modals in your App which your user can open and keeps all the Navigation Logic separate.

You can use the onGenerateRoute callback to define named routes.

return MaterialApp(

initialRoute: "/",

onGenerateRoute: routes,

); Route routes(RouteSettings settings) {

switch (settings.name) {

case '/':

return MaterialPageRoute(

builder: (_) => MainScreen(),

);

case '/preferences':

PrefClass pref = settings.arguments as PrefClass;

return MaterialPageRoute(

builder: (_) => PreferenceScreen(

prefClass: pref,

),

);

}

}

2. Navigating to Routes

Basic Navigation is pretty straightforward. So let’s talk about Navigating without context. 😕

Sometimes context may not be available for you to use (you may run into this thing as you try to organize your code). In that case, you need to create a GlobalKey<NavigatorState> and plug this into your MaterialApp widget like this (make it static to use it from anywhere).

static final navKey = GlobalKey<NavigatorState>(); return MaterialApp(

navigatorKey: navKey,

);

Now you can navigate like this 👌

navKey.currentState.pushNamed("/route", arguments: prefClass);

navKey.currentState.pop();

You can also do a similar thing to control a Scaffold by specifying the key in its optional ‘key’ parameter.

// earlier

Scaffold.of(context)

// by using GlobalKey<ScaffoldState>, this can be replaced by

scaffoldKey.currentState

You can even get the context (at that level) using navKey.currentContext

Networking

We need to make requests, show loaders, handle errors, convert the JSON responses to map efficiently and then update app UI to reflect the changes. We may even need to block the user from making more clicks and provide ways to cancel a request, handle timeouts, etc.

The Dio package is useful here.

1. Converting JSON to Map

Converting to JSON is simple, using the jsonDecode() method. However, your Flutter app runs in a single dart isolate and if we compute complex JSON in the same thread, UI performance is affected. So we must create a new isolate to do so. Doing so is simple using Dio and the easy to use compute function available in Flutter.

// Must be top-level function, i.e not within any class

_parseAndDecode(String response) {

return jsonDecode(response);

}



parseJson(String text) {

return compute(_parseAndDecode, text);

}



void main() {

Dio dio = Dio();

...

//Set the Custom jsonDecodeCallback

(dio.transformer as DefaultTransformer).jsonDecodeCallback = parseJson;

runApp(MyApp());

}

Read this article for more info about parsing complex JSON in Flutter.

Theming and Styling

Styling is available within widgets as well and is pretty straightforward to go about. So, let's talk about Global Theming.

It's best to use some tool to style the App globally, I highly recommend you to check out Panache — An open-source Flutter Material Theme Editor. You can use it online in your browser itself.

Here is the Repo on Github if you are interested.

With this tool, you can export the ThemeData directly as a dart file and then apply it to your MaterialApp like this. 🔥

return MaterialApp(

...

theme: ThemeData(

... // paste exported theme from Panache here

),



);

Global theming is something that will save you from writing a lot of code at a later stage(while styling your widgets). And using a web editor makes it fast and visual.

Organizing Code

First of all — Use git, period. Now let's talk about the directory structure.

We need a structue which is naturally readable, clearly indicates the starting point of the App and has a place for everything we’d need.

There can be a million different ways to do this. Like those mentioned in this, this, this and this. So here is my take on this (so you see, ‘this’ has always been a mystery, even before javascript 😆)

Main.dart —the Starting point of the app, Contains Top-level Providers, Global Theme.

Widgets — common widgets are placed in this, widgets which are screen-specific are placed under the widgets folder under the respective screen’s folder, e.g login_screen->widgets will have a login form widget.

Services — files under services should be made using ‘Singleton’ pattern so that one single instance can be used anywhere within the App

The rest of ‘this’ is pretty much self-explanatory.

Some Cool Packages and Tools