customizing flutters demo app

By | August 23, 2020

In this post we will continue the development of the flutter app you just created in the previous post.

Change control

First we will add this app to a GitHub account. Log into your GitHub account before continuing. If you don’t want to do this you can just refer to my GitHub repository at https://github.com/JimmySoftLLC/my_flutter_tutorial.

Most of the steps listed in this tutorial are also individual commits in my repository.

Next choose “VCS/Import into Version Control/Share Project on Github”.

The following screen will display with the files to be added for your initial commit. Press “Add” and it will create a repository on your GitHub account. If successful it will indicate the files are shared successfully.

Note: When I cloned my project to another computer I had to add a Flutter run configuration that points the my “main.dart” file. To add one choose “Run/Edit configurations”. Press the “+” button and choose “Flutter”. Enter “main.dart” for the “Name” and enter the path to your “main.dart” file for the “Dart endpoint”. When finished press “OK”.

Routing between home page and second page

The original demo app had everything in the “main.dart” file. We will refactor the “main.dart” file to have only one stateless widget defining the home page and second page routes. Then we will create separate files for the home page and second page.

Make two new files named “home_page.dart” and “second_page.dart” in the “lib” folder.

Paste the following code in “home_page.dart”

import 'package:flutter/material.dart';
import 'package:my_flutter_tutorial/second_page.dart';

class MyHomePage extends StatefulWidget {
  static const String id ='/MyHomePage';
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('My Flutter Tutorial',style: TextStyle(fontSize: 17,)),
          backgroundColor: Colors.orange,
          actions: <Widget>[
            // overflow menu
          ],
        ),
      bottomNavigationBar: BottomAppBar(
        child: new Row(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            FlatButton(
            color: Colors.orange,
            child: Text(
              "Second page",
              style: TextStyle(fontSize: 10.0),
            ),onPressed: (){
            Navigator.pushNamedAndRemoveUntil(context, MySecondPage.id,(Route<dynamic> route) => false);
            }),
          ],
        ),
      ),
      body: Center(
        child: Container(
        )
        ),
      );
  }

  @override
  void deactivate() {
    super.deactivate();
  }
}

Paste the following code in “second_page.dart”

import 'package:flutter/material.dart';
import 'package:my_flutter_tutorial/home_page.dart';
import 'package:flutter/cupertino.dart';

class MySecondPage extends StatefulWidget {
  static const String id ='MySecondPage';
  @override
  _MySecondPageState createState() => new _MySecondPageState();
}

class _MySecondPageState extends State<MySecondPage> {
  TextEditingController textEditingControllerUrl = new TextEditingController();
  TextEditingController textEditingControllerId = new TextEditingController();

  @override
  initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(
        appBar: AppBar(
          title: Text('My second page',style: TextStyle(fontSize: 17,)),
          backgroundColor: Colors.blue,
          actions: <Widget>[
            // overflow menu
          ],
        ),
        bottomNavigationBar: BottomAppBar(
          child: new Row(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              FlatButton(
                  color: Colors.blue,
                  child: Text(
                    "Home",
                    style: TextStyle(fontSize: 10.0),
                  ),onPressed: (){
                Navigator.pushNamedAndRemoveUntil(context, MyHomePage.id,(Route<dynamic> route) => false);
              }),
            ],
          ),
        ),
        body: SingleChildScrollView(
        ),
      ),
    );
  }
}

Replace the contents of “main.dart” with the following code. This code defines the routes to the HomePage and SecondPage using Ids.

import 'package:flutter/material.dart';
import 'package:my_flutter_tutorial/home_page.dart';
import 'package:my_flutter_tutorial/second_page.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        debugShowCheckedModeBanner: false,
        home:MyHomePage(),
        routes: {
          MyHomePage.id : (context) => MyHomePage(),
          MySecondPage.id : (context) => MySecondPage(),
        }
    );
  }
}

Run the app and you will be able to move from one page to the next.

When you press the “Second Page” button the following function is used “Navigator.pushNamedAndRemoveUntil(context, MyHomePage.id,(Route<dynamic> route) => false);”

This function “pushNamedAndRemoveUntil” removes all the other views and pushed the named page. This creates a single page application experience and prevents the user from swiping the view away which could put them on the wrong view.

Widgets

Everything in flutter is driven by widgets. The buttons currently don’t have any padding. To fix this we wrap the button in a padding widget.

To wrap a button with padding widget click on the line where button is. A light bulb will show where you can select “Wrap with padding”. Boy, that was easy!

The button will now have padding as defined with EdgeInsets. There are other types of EdgeInsets. For example EdgeInset.fromLTRB gives you left, top, right and bottom settings.

Padding(
              padding: const EdgeInsets.all(8.0),
              child: FlatButton(
              color: Colors.orange,
              child: Text(
                "Second page",
                style: TextStyle(fontSize: 10.0),
              ),onPressed: (){
              Navigator.pushNamedAndRemoveUntil(context, MySecondPage.id,(Route<dynamic> route) => false);
              }),
            ),

Many of the widgets are similar to layout classes you are already familiar with in bootstrap or materialUI. These include containers, rows, columns, flex grids and more.

Hot reload

Hot reload allows you to change the code on the fly and the app automatically updates. This is by far my favorite feature and helps speed up development. Change some colors, wrap things with widgets, change functions and have fun.

Context

Widget builders and navigator functions have context and must be passed when going to a new page.

For example a function that validates some data and then navigates to a view will need to pass context.

This next example will demonstrate this.

Add “context_example.dart” to your project and add the following code.

import 'package:flutter/material.dart';
import 'package:my_flutter_tutorial/home_page.dart';

void updateSomeData(myData, context) async {
    // do stuff with myData
    Route _createRoute() {
      return PageRouteBuilder(
        pageBuilder: (context, animation, secondaryAnimation) => MyHomePage(),
        transitionsBuilder: (context, animation, secondaryAnimation, child) {
          return FadeTransition(opacity: animation, child: child);
        },
      );
    }
    Navigator.of(context).pushAndRemoveUntil(_createRoute(),(Route<dynamic> PageRouteBuilder) => false);
}

Import “context_example.dart” into your “second_page.dart” file and add another button as follows.

Padding(
                padding: const EdgeInsets.all(8.0),
                child: FlatButton(
                    color: Colors.blue,
                    child: Text(
                      "updateSomeData then home",
                      style: TextStyle(fontSize: 10.0),
                    ),onPressed: (){
                  updateSomeData(null, context);
                }),
              ),

The second button named “updateSomeData then home” will call a function that has context as one of its parameters. The function can then pass this context to the home page.

State

Local state is easily handled in flutter. You simply create a local variable in your class and then the widget can update it using “onChanged: (value) {myVariable = value;}”

Plugins

Plugins extend functionality of Flutter. Plugins are published at https://pub.dev/ and are rated by likes, pub points and popularity.

Plugins come from many sources: the flutter team, other companies and developers.

Navigate to https://pub.dev/ and search for the a plugin your are interested in.

For this example we will add font awesome icons to our project.

To use this plug modify your pubspec.yaml file as shown. Note: Indentation is important in “yaml” files so make sure it is consistent.

dependencies:
  flutter:
    sdk: flutter
  font_awesome_flutter: ^8.8.1
  cupertino_icons: ^0.1.3

The next step is to import the package into your dart file. So add this line to all pages where you want to use font awesome icons. “import ‘package:font_awesome_flutter/font_awesome_flutter.dart’;”

After changing your dart files you should get a message to “Get dependencies”. If you don’t you can manually get them by typing “flutter pub get” in the Android Studio terminal.

Custom widgets

You can create custom widgets for your applications. I like to create “card” widgets that can put into a list. The example below shows them added to this project. You can check this out in the GitHub repository.

In Conclusion

Flutter is a fun IDE to use. I am hoping someday they will release the web version. Currently react native can do similar things except their hot reload feature is not as fast. React native now has competition so both products can get better. A faster hot reload for react native? One can hope. The future will be exciting for both frameworks.