Categories
dart flutter software_design software_dev

A solid architecture is the bedrock of your app

— WIP — but publishing forces me to come back and continue!

If you can nail a good architecture it makes all sorts of things possible, whereas a bad architecture can weigh like an anchor on your development.

In the last post on software architecture, I showed a solution that I would only describe as lack of architecture. In this post I plan on laying the groundwork for a solid architecture to the WeatherSdbx app. Architecture can also be referred to as software design and describes the inner workings of the app. I’ve found that users/shareholders are usually more concerned about the UI design of an app, which is certainly important, but a well thought out architecture can help the development, testing and deployment move much more smoothly.

The sketch above captures a couple ideas (along with a doodle or three) for the WeatherSdbx architecture and a general one I use for most of my Flutter apps. The central component is the AppMgr which connects the UI to the app components and contains the business logic to run the application.

The components of ver0.1.0 are as follows:

  • main app to configure setup
  • home screen
  • current observation widget
  • app mgr

The goal of V0.1.0 is to setup a structure which can be expanded on in future versions with more functionality such as storage, http communication and so forth. One of the goals is to have a structure that functional components can be added to separately, maybe even just stubbed out, to make the entire app easier to build and test.

Categories
doodle

SDoodle

Categories
Uncategorized

Fired up the LED cube

Categories
dart flutter software_dev

Dart extension experiment

I had a use for Dart extensions, but after playing with the code, I found I could make it more compact with an existing method.

I work with an app which utilizes colors at 10% opacity. This sounds like a great place to use an extension on the Color object.

extension ColorOpacity on Color {
  Color get opacity10 => withOpacity(0.1);
}

This works great…and I can’t help play around with the idea. Why not multiple opacity entries to show off the range of a given color.

const Color colorCoolBlue = Color(0xFF3BB2E2);

extension ColorOpacity on Color {
  Color get opacity75 => withOpacity(0.75);
  Color get opacity50 => withOpacity(0.5);
  Color get opacity25 => withOpacity(0.25);
  Color get opacity10 => withOpacity(0.1);
}

With a simple widget to show off the results.

import 'package:flutter/material.dart';
import 'package:weathersdbx/ui/ui_const.dart';

///
/// Simple screen to try out some color ideas
///
class ExpColorsWidget extends StatelessWidget {
  const ExpColorsWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    Widget _constructColorSlice(Color color) => Expanded(
          child: Container(
            color: color,
          ),
        );
    Widget _constructBody() {
      return SizedBox(
        height: 200,
        child: Row(
          children: [
            _constructColorSlice(colorCoolBlue),
            _constructColorSlice(colorCoolBlue.opacity75),
            _constructColorSlice(colorCoolBlue.opacity50),
            _constructColorSlice(colorCoolBlue.opacity25),
            _constructColorSlice(colorCoolBlue.opacity10),
          ],
        ),
      );
    }

    return Scaffold(
      appBar: AppBar(
        title: const Text("Colors Experiment"),
      ),
      body: _constructBody(),
    );
  }
}

Have to admit I was a bit challenged to get all the rows to have the same width without hardcoding until I found this answer on StackOverflow thanks to diegoveloper about using the Expanded widget.

The method constructBody can further be refactored to move the color row to a separate method.

Widget _constructColorRow(
  Color color, {
  double height = 200,
}) =>
    SizedBox(
      height: height,
      child: Row(
        children: [
          _constructColorSlice(color),
          _constructColorSlice(color.opacity75),
          _constructColorSlice(color.opacity50),
          _constructColorSlice(color.opacity25),
          _constructColorSlice(color.opacity10),
        ],
      ),
    );

Widget _constructBody() {
  return _constructColorRow(colorCoolBlue);
}

And if feels like the next progression of this little experiment is to allow multiple colors, and, if we can expand one way…why not both?

const colorCoolBlue = Color(0xFF3BB2E2);
const colorDeepRed = Color(0xFF940000);

Widget _constructColorRow(
  Color color) =>
    Expanded(
      child: Row(
        children: [
          _constructColorSlice(color),
          _constructColorSlice(color.opacity75),
          _constructColorSlice(color.opacity50),
          _constructColorSlice(color.opacity25),
          _constructColorSlice(color.opacity10),
        ],
      ),
    );


Widget _constructBody() => Column(
      children: [
        _constructColorRow(colorCoolBlue),
        _constructColorRow(colorDeepRed),
      ],
    );

Now the opacity extension feels restricting, the following code goes back to the Color.withOpacity method to lift this restriction.

const defaultOpacityList = [1.0, 0.75, 0.5, 0.25, 0.10];

Widget _constructColorRow(Color color,
        {opacityList = defaultOpacityList}) =>
    Expanded(
      child: Row(children: [
        ...opacityList
            .map(
              (opacity) => _constructColorSlice(color.withOpacity(opacity)),
            )
            .toList()
      ]),
    );

Finally, adding a Dart data class to define the color and its opacities, the code has even more versatility. A further step could easily take the data out of the code altogether into something like JSON for further functionality.

class ColorDef {
  final Color baseColor;
  final List<double> opacityList;

  ColorDef(this.baseColor, this.opacityList);
}

Full final screen, exp_colors_widget.dart, can be found in my blogsdbx repository.