How to build a CustomScaffold with alternative build methods in Flutter
Tags: dart, flutter, dry, mobile
Published: 2020-09-30 09:22:03

You learned the basics of Flutter. You already built a project of your own. But you feel it, this sensation - it is time to level up. Do not worry, we're not going too fast but a step at a time.

🎯 In this tutorial you will understand
* how to create your own custom widget
* how to use **typdef**
* how to add alternative building methods to your custom widget

# Level 1 - 🛶
The first approach we all learned was to just place a `Scaffold` widget and continue from there.

#####main.dart
```dart
class App extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    final container = Container(color: Colors.red, width: 100, height: 100);

    return MaterialApp(
      title: 'Bare Scaffold',
      home: Scaffold(body: container),
    );
  }
}
```

and the result is below:

![A Scaffold With a 100x100px red container partially hidden by the statusbar](https://raw.githubusercontent.com/didof/flutter_custom_scaffold_via_typedf/master/custom_scaffold_1.png)

We immediately notice a problem: part of the red container is hidden beneath the status bar. Here the use of SafeArea is useful:

#####main.dart
```dart
...
return MaterialApp(
      title: 'Scaffold with Safe Area',
      home: Scaffold(
        body: SafeArea(
          child: container,
        ),
      ),
    );
...
```

that yelds the following:

![A Scaffold With a 100x100px red container not hidden by the statusbar anymore](https://raw.githubusercontent.com/didof/flutter_custom_scaffold_via_typedf/master/custom_scaffold_2.png)


Here you catch the first drawback: following this pattern, in any further screen, you will have to repeat this code. I do not know you, but surely I do not like the repeat myself.

# Level 2 - ⛵
Somewhere you heard of the *DRY* concept. Let's use it.

#####custom_scaffold.dart
```dart
import 'package:flutter/material.dart';

class CustomScaffold extends StatelessWidget {
  final Widget body;

  const CustomScaffold({this.body});

  @override
  Scaffold build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: body,
      ),
    );
  }
}
```

so that we can instantiate it like so:

#####main.dart
```dart
class App extends StatelessWidget {
  final container = Container(color: Colors.red, width: 100, height: 100);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Custom Scaffold .alternative',
        home: CustomScaffold(body: container));
  }
}
```

Nothing changed on the output. But we spared some lines and potentially, we could spare much more of them (think at dynamically injected providers).

But now we want to find a way to easily operate on layouts and more specifically design them according to their purpose.

# Level 3 - 🚤

Avoiding depriving ourselves of what we have built, let's implement an alternative way of creating the output.
`LayoutBuilder` is a good candidate since it gives us a wide range of constraints. But this use case is slightly different from one adopted in `SafeArea`. `LayoutBuilder` creates the output not by injecting the child; it actually uses a builder function that puts comfortable tools in our hand, *constraints* (other than context). 
So you try to add a builder method to your own `CustomScaffold`, and it will also easily provide you with constraints (all without losing the fundamental functionality of SafeArea).

The first step is to create a signature for a function that will generate a Widget. Something that you normally put before params in function's declaration such as `String`, `int`, or `myRandomWidget`.

#####custom_scaffold.dart
```dart
typedef CustomBuilder = Widget Function(
  BuildContext context,
  double x,
  double y,
);
```

Now get to the actual CustomScaffold. Similarly to how we defined a `final Widget body` (therefore also defined in the constructor), we also can define a `final CustomBuilder builder`. For those new to `typdef`s it is like adding a `Function` property but a more specific function, precisely defined by us.

#####custom_scaffold.dart
```dart
class CustomScaffold extends StatelessWidget {
  final Widget body;
  final CustomBuilder builder; #1

  const CustomScaffold({this.body, this.builder}); #2

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: LayoutBuilder(builder: (context, constrains) {
          return builder(context, constrains); #3
        }),
      ),
    );
  }
}
```

Adding a `LayoutBuilder` in the stack, we are provided with constraints that we can pass to our builder function (eventually you can consider modulating them or mixing them with something else to create something new, but for the sake of simplicity I leave this honor to you).

There is it. I don't know you, I felt sincere happiness the first time I used a HOF. Let's wrap to where we use it, so to say.

#####main.dart
```dart
class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Custom Scaffold .alternative',
      home: CustomScaffold(
        builder: (context, constraints) {
          return Container(
            color: Colors.blue,
            width: constraints.maxWidth / 2,
            height: constraints.minHeight / 3,
          );
        },
      ),
    );
  }
}
```

We give it a builder (to keep in mind that a Scaffold lives without problems even without a body, (🧟), it's not `@required` - and it gives as context and constraints. It seems fair to me.
And the result:

![A Scaffold with a yellow responsive container](https://raw.githubusercontent.com/didof/flutter_custom_scaffold_via_typedf/master/custom_scaffold_3.png)

You see how easily you can arrange the layout, don't you?

# Level 4 - 🛥

Do you know how you can use `ListView()` but also `ListView.builder()`? It's easy to implement.
#####custom_scaffold.dart
```dart
class CustomScaffold extends StatelessWidget {
  final Widget body;
  final CustomBuilder builder;

  const CustomScaffold({this.body, this.builder});

  static Widget responsive({CustomBuilder builder}) {
    return _buildSafeScaffold(
      child: LayoutBuilder(builder: (context, constraints) {
        return builder(context, constraints);
      }),
    );
  }

  static _buildSafeScaffold({Widget child}) {
    return Scaffold(
        body: SafeArea(
      child: child,
    ));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: body,
      ),
    );
  }
}

```
> Note how the regular build method (the one with the `@override` meta) is untouched - we could still instantiate `CustomScaffold()` and pass to it a body.

But we also get an alternative,  `CustomScaffold.responsive()`, that gives us the constraints:

#####main.dart
```dart
class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Custom Scaffold .responsive',
      home: CustomScaffold.responsive(
        builder: (context, constraints) {
          return Container(
            color: Colors.yellow,
            width: constraints.maxWidth / 4,
            height: constraints.maxHeight / 2,
          );
        },
      ),
    );
  }
}
```

***

Now that you get how to "hide" different recurring features behind your custom widget, I leave you free to experiment on your own.

We certainly won't make code history with this ability, 
but if you have come this far it is very likely that this possibility was not very familiar to you. So it's a step forward. And that's enough.

### Contact me
🐤 https://twitter.com/did0f