Build a flutter application with provider

In our last section on the usage of Provider package, we’ve seen what we can do with Provider. To tell you frankly, we can do many things in Flutter with one single package Provider. In this section, we’ll try to build a flutter application, a blog, with the help of Provider package.

Just for little recapitulation let us first remember what Provider is. Provider package in Flutter serves us in many purposes, that also includes passing a global style or data across the flutter app. We can pass the provided value through child constructors.

While we build our simple blog application in Flutter, we’ll pass both. A global theme and blog posts through widget class constructors.

However, let me warn you at the very beginning. The way we’re going to build a Blog app by passing provided value through constructors is purely for teaching purpose. It will help the Flutter beginners to understand a few key concepts. The same Blog app can be built using less code; yes, that is possible.

In fact, we’ve discussed this in the next section – Should I use for loop in Flutter? Therefore feel free to take a look.

Firstly, to start with we should keep our Provider above the root app, just like the following code.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'model/global_theme.dart';
import 'model/blog_post.dart';
import 'view/home.dart';

void main() {
  runApp(
    /// Providers are above [Root App] instead of inside it, so that tests
    /// can use [Root App] while mocking the providers
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => GlobalTheme()),
        Provider<List<BlogPost>>(
          create: (context) => blogPosts,
        ),
      ],
      child: const Home(),
    ),
  );
}

Secondly, we’ve used multi provider and for the first generic type global theme, we’ve used ChangeNotifierProvider. For the second generic type for our Provider we use a List whose type is BlogPost model class.

Provider<List<BlogPost>>(
          create: (context) => blogPosts,
        ),

Finally, our first child is the Home widget.

Next, we’ll take a look at the model classes.

Firstly, the global theme, which we have defined like below.

import 'package:flutter/material.dart';

class GlobalTheme with ChangeNotifier {
  final globalTheme = ThemeData(
    primarySwatch: Colors.deepOrange,
    textTheme: const TextTheme(
      bodyText1: TextStyle(
        fontSize: 22,
      ),
      bodyText2: TextStyle(
        color: Colors.blue,
        fontSize: 18,
        fontWeight: FontWeight.bold,
      ),
      caption: TextStyle(
        fontSize: 16,
        fontWeight: FontWeight.bold,
        fontStyle: FontStyle.italic,
      ),
      headline1: TextStyle(
        color: Colors.deepPurple,
        fontSize: 70,
        fontWeight: FontWeight.bold,
        fontFamily: 'Allison',
      ),
      headline2: TextStyle(
        color: Colors.deepOrange,
        fontSize: 25,
      ),
    ),
    appBarTheme: const AppBarTheme(
      backgroundColor: Colors.amber,
      // This will control the "back" icon
      iconTheme: IconThemeData(color: Colors.red),
      // This will control action icon buttons that locates on the right
      actionsIconTheme: IconThemeData(color: Colors.blue),
      centerTitle: false,
      elevation: 15,
      titleTextStyle: TextStyle(
        color: Colors.deepPurple,
        fontWeight: FontWeight.bold,
        fontSize: 30,
      ),
    ),
  );
}

As a result, our ChangeNotifierProvider takes that generic value. And its named parameter “create” returns the GlobalTheme model class, which in return defines our custom ThemeData object.

ChangeNotifierProvider(create: (_) => GlobalTheme()),

On the other hand, secondly, we have created another model class on Blog posts and added three posts there as List items. And, finally we’ll map that list with the help of provided value.

class BlogPost {
  final String id;
  final String title;
  final DateTime date;
  final String content;

  BlogPost({
    required this.id,
    required this.title,
    required this.date,
    required this.content,
  });
}

final blogPosts = [
  BlogPost(
    id: 'b1',
    title: 'First Post',
    date: DateTime(2020, 1, 2),
    content: 'This is First post. So it is better not to write more.',
  ),
  BlogPost(
    id: 'b2',
    title: 'Second Post',
    date: DateTime(2020, 2, 15),
    content: 'This is Second post. Sorry for the delay in posting.',
  ),
  BlogPost(
    id: 'b3',
    title: 'Third Post',
    date: DateTime(2020, 3, 25),
    content: 'This is Third post. Again delay! Why? Because Words evade me.',
  ),
];

Nothing to mention or explain so far. Because we’ve used Provider, we can separate our business logic. Moreover, anywhere in the widget tree, any child widget can access either our global theme, or blog post data which are nothing but the provided value.

In our multi provider we’ve seen that our first child is Home widget.

Let’s take a look at our first child widget Home, which is a Material App, because we plan to use AppBar, so that we can move between home page and blog page by the back icon, which will be red. In our custom ThemeData model class , where we have defined that already.

// This will control the "back" icon
      iconTheme: IconThemeData(color: Colors.red),

Here is the child Home widget.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '/model/global_theme.dart';
import 'home_page.dart';

class Home extends StatelessWidget {
  const Home({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final ThemeData globalTheme = Provider.of<GlobalTheme>(context).globalTheme;
    return MaterialApp(
      title: 'Building Blog',
      theme: globalTheme,
      home: const HomePage(),
    );
  }
}

Here in the above code, we’ve declared a ThemeData object and get our custom theme style by using Provider.

final ThemeData globalTheme = Provider.of<GlobalTheme>(context).globalTheme;

After that the material app theme parameter takes that value.

theme: globalTheme,

Now, we can use this theme in many ways. The default Theme.of(context) will now be overridden by this custom ThemeData object globalTheme.

However, we’ve not taken that route. Instead in our next child HomePage widget, we declare another ThemeData object to show that we can use different custom theme classes, and apply them anywhere in the widget tree.

Let’s see the next child widget HomePage.

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import '/model/global_theme.dart';
import '/model/blog_post.dart';

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final homeTheme = Provider.of<GlobalTheme>(context).globalTheme;
    return Scaffold(
      appBar: AppBar(
        title: Text(
          'Blog Home Page',
          style: homeTheme.appBarTheme.titleTextStyle,
        ),
      ),

      /// pushing again to test
      body: HomeBody(
        homeTheme: homeTheme,
      ),
    );
  }
}

We’ve applied our global theme to the AppBar. Moreover, we’ve passed that custom ThemeData object through the next child constructor widget HomeBody.

As a result, our widget tree is getting bigger. However, we’re managing our provided data as we’re passing the value through constructors.

Again, the HomeBody child widget passes the same data to its child widget through constructors to show all blog posts on the home screen.

class HomeBody extends StatelessWidget {
  final ThemeData homeTheme;
  const HomeBody({Key? key, required this.homeTheme}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: ListView(
        children: [
          Container(
            margin: const EdgeInsets.all(5),
            padding: const EdgeInsets.all(5),
            child: Text(
              'Blog by Clumsy Coder',
              style: homeTheme.textTheme.headline1,
            ),
          ),
          Container(
            margin: const EdgeInsets.all(5),
            padding: const EdgeInsets.all(5),
            child: Text(
              'I am a clumsy coder who neither can write decent code, nor '
              'can write fiction.'
              ' That friction disturbs me a lot. '
              'But I try to be a good human being.',
              style: homeTheme.textTheme.headline2,
            ),
          ),
          const BlogPostHome(),
        ],
      ),
    );
  }
}

class BlogPostHome extends StatelessWidget {
  const BlogPostHome({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final blogPosts = Provider.of<List<BlogPost>>(context);
    return Column(
      children: blogPosts
          .map(
            (e) => BlogPostOutput(
              id: e.id,
              title: e.title,
              date: e.date,
              content: e.content,
            ),
          )
          .toList(),
    );
  }
}

class BlogPostOutput extends StatelessWidget {
  final String id;
  final String title;
  final DateTime date;
  final String content;

  const BlogPostOutput({
    Key? key,
    required this.id,
    required this.title,
    required this.date,
    required this.content,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        InkWell(
          onTap: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => BlogDetail(
                  id: id,
                  title: title,
                  date: date,
                  content: content,
                ),
              ),
            );
          },
          child: Text(
            title,
            style: const TextStyle(
              fontSize: 30,
              fontWeight: FontWeight.bold,
            ),
          ),
        ),
      ],
    );
  }
}

Let’s run the app, and the home page displays all the blog posts and looks like the following where we get the clickable title of the posts.

Flutter application with provider displays all posts on the home page
Flutter application with provider displays all posts on the home page

We have a description about the blogger and used our custom theme to look the text different than the default theme that comes with Flutter.

Moreover, using Provider we’ve got each blog posts and pass them as provided value through constructors to the next child widget, which will display the single post if we click any post title.

Flutter blog application with provider shows single post
Flutter blog application with provider shows single post

Now, we can click the red back icon in the AppBar and go back to the home page.

The single post blog page code is like the following. The provided value travels through the constructors of child widget.

And, we’ve applied our custom theme style on each value so that they look different.

class BlogDetail extends StatelessWidget {
  final String id;
  final String title;
  final DateTime date;
  final String content;
  const BlogDetail({
    Key? key,
    required this.id,
    required this.title,
    required this.date,
    required this.content,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final blogTheme = Provider.of<GlobalTheme>(context).globalTheme;
    return Scaffold(
      appBar: AppBar(
        title: Text(
          'Blog Page',
          style: blogTheme.appBarTheme.titleTextStyle,
        ),
      ),
      body: Center(
        child: ListView(
          children: [
            Container(
              margin: const EdgeInsets.all(5),
              padding: const EdgeInsets.all(5),
              child: Text(
                title,
                textAlign: TextAlign.left,
                style: blogTheme.textTheme.headline1,
              ),
            ),
            Container(
              margin: const EdgeInsets.all(5),
              padding: const EdgeInsets.all(5),
              child: Text(
                content,
                textAlign: TextAlign.left,
                style: blogTheme.textTheme.bodyText1,
              ),
            ),
            Container(
              margin: const EdgeInsets.all(5),
              padding: const EdgeInsets.all(5),
              child: Text(
                DateFormat('d MMMM y').format(date),
                style: blogTheme.textTheme.bodyText2,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

This blog app can be built in a different way. In the next section we’ll look into that matter as we progress to learn other usages of Provider package.

To get the full code snippet please visit respective GitHub repository.

So, stay tuned and happy fluttering.

What Next?

Books at Leanpub

Books in Apress

My books at Amazon

GitHub repository

Technical blog

Twitter

Comments

2 responses to “Build a flutter application with provider”

  1. […] although we’ve built the same flutter application, yet they are different in […]

  2. […] In this section we’ll build the Flutter Chat app UI. In addition we’ll also write the Business logic with Provider package. […]

Leave a Reply