Should I use for loop in Flutter

Let us answer the question at the very beginning. Yes, we can use for loop in Flutter. And that too by maintaining the declarative style.

In fact, we’ll see in a minute, how we can reduce our code size using for loop.

In our last section we’ve built a simple blog app with the help of Provider package that only displays a few clickable posts on home page. Consequently, we can click any post and read the post on another screen.

However, we’ve passed the provided value through class constructors. As a result, we need to pass around a lot of data across our app.

With reference to the above statement, we can say that we should avoid this.

Why?

Because we can reduce the boilerplate and manage the same thing by using for loop in proper place. And there are other methods too, that we’ll see in the coming section.

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.

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

Certainly, we can pass the provided value through child constructors. But, as we just told, we need to be careful about not making it too much.

Before we start building the blog app using for loop, I strongly recommend to compare these two GitHub repositories.

Let us put the Providers above the root app.

import 'package:build_blog_with_flutter/model/blog_post.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'model/global_theme.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(),
    ),
  );
}

Now, we can use two types of provided values. The first one is a custom theme. And the second provided value is the List of blog posts.

In our Multi provider we have the first child the 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: 'Flutter Demo',
      theme: globalTheme,
      home: const HomePage(),
    );
  }
}

The above code shows us how we can use Provider.of(context) to get our custom theme object.

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

We’re not going to pass this ThemeData object across the app through class constructors anymore. Because we’ve used Material App named parameter “theme” to adopt the custom ThemeData.

theme: globalTheme,

As a result, we can use Theme.of(context) across the app to apply our custom ThemeData.

The Material App has the child widget HomePage. Therefore, we can take a look at the Home Page widget.

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          'Blog Home Page',
          style: Theme.of(context).appBarTheme.titleTextStyle,
        ),
      ),
      body: const HomeBody(),
    );
  }
}

Firstly, we’ve started applying our custom theme style in our AppBar by Theme.of(context).

style: Theme.of(context).appBarTheme.titleTextStyle,

Next we create another child widget HomeBody, where we can get the all provided value using Provider.of(context).

How can I use for loop in flutter?

Once we get the blog posts object using Provider.of(context), we can now iterate through the List items.

Let’s see the code, which will give us an idea, how we can now easily pass the Blog posts object to the child widget in our widget tree.

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

  @override
  Widget build(BuildContext context) {
    final posts = Provider.of<List<BlogPost>>(context);
    return Center(
      child: ListView(
        children: [
          Container(
            margin: const EdgeInsets.all(5),
            padding: const EdgeInsets.all(5),
            child: Text(
              'Blog by Clumsy Coder',
              style: Theme.of(context).textTheme.headline2,
            ),
          ),
          Container(
            margin: const EdgeInsets.all(5),
            padding: const EdgeInsets.all(5),
            child: Text(
              'I am a clumsy coder trying to write decent code, and '
              'fiction.'
              ' But there is a friction. '
              'So, I avoid proper diction.',
              style: Theme.of(context).textTheme.headline1,
            ),
          ),
          for (var post in posts) BlogPostHome(post: post),
        ],
      ),
    );
  }
}

Now using for loop, we can pass the each Blog post object through the child widget BlogPostHome constructor. So that these two widgets can jointly display the blog home page.

class BlogPostHome extends StatelessWidget {
  final BlogPost post;
  const BlogPostHome({
    Key? key,
    required this.post,
  }) : super(key: key);

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

Nothing fancy in the above code, except that we’ve used an InkWell widget so that we can click and navigate to another page to read any blog.

If we run the app, we can view all the clickable posts in one place.

Displaying blog posts using for loop in flutter
Displaying blog posts using for loop in flutter

So far, we’ve hard coded three blog posts in our Blog posts model class. Let’s take a look at the code.

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.',
  ),
];

Let us click the third post and see how it looks like on the single blog post page.

Single blog post page
Single blog post page

As we can see that we’ve consistently applied our custom theme style everywhere, including the red back icon in the AppBar. We’ve defined that property in our custom Theme model class. Let’s take a quick look.

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: 50,
        fontFamily: 'Allison',
      ),
      headline2: TextStyle(
        color: Colors.deepOrange,
        fontSize: 30,
        fontWeight: FontWeight.bold,
      ),
    ),
    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,
        fontFamily: 'Allison',
        fontSize: 40,
      ),
    ),
  );
}

Lastly, we should see the code snippet of the single blog post page.

class BlogDetail extends StatelessWidget {
  final BlogPost post;
  const BlogDetail({
    Key? key,
    required this.post,
  }) : super(key: key);

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

You can always take a look at the respective GitHub repository.

We’ve successfully reduced the code size. In the previous section, where we have passed provided values through class constructors, we had written 187 lines of code. Here, using for loop reduces that length to 139 lines of code.

Certainly, we’ll try to find another approach that might reduce the code line more.

So stay tuned and happy fluttering.

What Next?

Books at Leanpub

Books in Apress

My books at Amazon

GitHub repository

Technical blog

Twitter

Comments

Leave a Reply