Blog · · 2 min read

Flutter ListView Tutorial – WeightTracker 1

In this post, we are going to create a basic ListView in Flutter with dynamically added values. Let’s get to it! 🙂

Simple ListView

home_page.dart
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<String> strings = [];
void _addWeightEntry() {
setState(() => strings.add("new string"));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Weight Tracker'),
),
body: ListView(
children: strings.map((String string) {
return Text(string);
}).toList(),
),
floatingActionButton: FloatingActionButton(
onPressed: _addWeightEntry,
tooltip: 'Add new weight entry',
child: Icon(Icons.add),
),
);
}
}

This code results in simplest list you could imagine.

ListView widget is our main list container (surprising!). It contains many properties but for now we will focus on only one: children.

ListView.children is where we provide our list of Widgets to be displayed. In order to do it we simply map previously initialized list of Strings to list of Rows containing only Text with our value.

In that way we end up with the list view presented on the right.

Now let’s try to make some non-hello-world look.

Preparing model

weight_save.dart
class WeightSave {
DateTime dateTime;
double weight;
WeightSave(this.dateTime, this.weight);
}

Simple model for entry used to weight tracking.

Preparing layout

weight_list_item.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:weight_tracker/model/WeightSave.dart';
class WeightListItem extends StatelessWidget {
final WeightSave weightSave;
final double weightDifference;
WeightListItem(this.weightSave, this.weightDifference);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Expanded(
child: Column(
children: [
Text(
DateFormat.yMMMMd().format(weightSave.dateTime),
textScaleFactor: 0.9,
textAlign: TextAlign.left,
),
Text(
DateFormat.EEEE().format(weightSave.dateTime),
textScaleFactor: 0.8,
textAlign: TextAlign.right,
style: TextStyle(color: Colors.grey),
),
],
crossAxisAlignment: CrossAxisAlignment.start,
),
),
Expanded(
child: Text(
weightSave.weight.toString(),
textScaleFactor: 2.0,
textAlign: TextAlign.center,
),
),
Expanded(
child: Text(
weightDifference.toString(),
textScaleFactor: 1.6,
textAlign: TextAlign.right,
),
),
],
),
);
}
}

The layout root is a padding which is basically a wrapper for our layout to make it not touching each other.

A padding’s child is a Row containing 3 children. Rows allow us to stack Widgets horizontally.

To make every child of a Row cover exactly one-third of whole space, every element is wrapped into Expanded.

The left column is actually a Column widget. Columns are also tools for stacking multiple widgets but they do it vertically. In Column.children there are two Texts representing exact date and a day of the week of our entry.

Middle and right columns are simple Texts. The middle one is designed to represent saved weight value while the right one provides us the difference from the previous entry.

Wrapping it up

To use our new layout we simply replace previous Row with WeightListItem instance (remember to add an import).

home_page.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:weight_tracker/WeightListItem.dart';
import 'package:weight_tracker/model/WeightSave.dart';
class HomePage extends StatefulWidget {
HomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_HomePageState createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<WeightSave> weightSaves = [];
void _addWeightEntry() {
setState(() {
weightSaves.add(new WeightSave(
new DateTime.now(), new Random().nextInt(100).toDouble()));
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView(
children: weightSaves.map((WeightSave weightSave) {
//calculating difference
double difference = weightSaves.first == weightSave
? 0.0
: weightSave.weight -
weightSaves[weightSaves.indexOf(weightSave) - 1].weight;
return WeightListItem(weightSave, difference);
}).toList(),
),
floatingActionButton: FloatingActionButton(
onPressed: _addWeightEntry,
tooltip: 'Add new weight entry',
child: Icon(Icons.add),
),
);
}
}

And that’s it!

I hope you liked that post!

You can find the whole project here.

Cheers 🙂