Routing to Dynamic Pages with Flutter
I had a conversation with a friend of mine over the past week about creating dynamic pages for your flutter application and I thought to share.
I recall encountering the same issue while I was starting out with Flutter. It was my first e-commerce application. I spent all night rewriting the same code. Omo, it wasn’t funny. I press CTRL C tire
There is a quote attributed to Andy Hunt and Dave Thomas in their book titled “The Pragmatic Programmer” which goes as follows:
“DRY — Don’t Repeat Yourself: Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.”
That is, if you find yourself writing the same code multiple times, it suggests that there might be an opportunity to refactor your code and extract common functionality into reusable components, functions, or classes. By doing so, you can improve maintainability, reduce bugs, and make your code more efficient and effective.
Having stated this, let’s break down the topic
Routing in Flutter is a mechanism that manages the navigation flow of an app. It allows users to move from one screen to another, enabling them to interact with different parts of the application.
However, dynamic pages, in this context, mean that the content or data displayed on these pages is not hard-coded or static but rather generated on the fly based on user input, data from APIs, or other sources.
Let’s see a very good example:
In this image, we have three screens.
- The Onboarding Page
- The Home Page displays a list of products(sneakers)
- The details page contains further information about each detail
What is the flow cycle of the three screens?
The User slides the screen from the onboarding page and navigates to the home page. If the user clicks on any of the products, it routes the user to the details page. This is a simple explanation of routing
Routing defines the entire life cycle of your app so before you build your application, take out time to properly plan the workflow. Unless you won’t get paid and you would end up eating your groceries and your floating berries.
The question then is that what if you have about 100 products in your home and you would love to create a details page for each product, would you create 100 dart files?
To do this, you would create a dynamic screen with dynamic parameters that update the UI every time it is being used or called.
In this tutorial, I will be showing you how this can be done while
Let’s start building
Let’s create a new Flutter project
flutter create e_commerce app
Let’s create a class that handles our list of “Products”
Class Products{
final String image;
final String name;
final String description;
final double price;
double rating;
Products({
required this.image,
required this.name,
required this.description,
required this.price,
this.rating = 0.0, // Default rating is set to 0.0
});
}
Let’s use this in our UI
import 'package:flutter/material.dart';
class ECommerceApp extends StatelessWidget {
final List<Products> productList = [
Products(
image: 'https://example.com/product1-image.png',
name: 'Product 1',
description: 'Description of Product 1',
price: 29.99,
rating: 4.2,
),
Products(
image: 'https://example.com/product2-image.png',
name: 'Product 2',
description: 'Description of Product 2',
price: 49.99,
rating: 3.8,
),
Products(
image: 'https://example.com/product3-image.png',
name: 'Product 3',
description: 'Description of Product 3',
price: 19.99,
rating: 4.5,
),
Products(
image: 'https://example.com/product4-image.png',
name: 'Product 4',
description: 'Description of Product 4',
price: 39.99,
rating: 4.0,
),
Products(
image: 'https://example.com/product5-image.png',
name: 'Product 5',
description: 'Description of Product 5',
price: 59.99,
rating: 4.8,
),
];
@override
Widget build(BuildContext context) {
// Here you can implement your app UI using Scaffold, ListView, etc.
// For simplicity, I'm just printing the product names here.
This is a basic stateless widget that takes the list of products from the Product class we created earlier. You could adjust the properties to whatever you choose.
return Scaffold(
appBar: AppBar(title: Text('E-Commerce App')),
body: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: productList.length,
itemBuilder: (context, index) {
final product = productList[index];
return ListTile(
leading: Image.network(product.image),
title: Text(product.name),
subtitle: Text(product.description),
trailing: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('\$${product.price.toStringAsFixed(2)}'),
Text('Rating: ${product.rating}'),
],
),
);
},
),
);
Here we created in our scaffold, a list view whose direction is horizontal which has a list title that takes the name of the product as its title, the image of the product amongst other properties.
As a flutter engineer, your job is to ensure that the user doesn’t only see the products but the user can click on each of the items and get to have another page that gives the full description of each screen.
How do you get that done without rewriting your codes?
Create just one screen that handles all the updates and logic as regards the UI.
To have this done, create a new Dart file to represent the product details screen. Let’s call it product_details_screen.dart
. On this screen, you'll display the details of the selected product.
import 'package:flutter/material.dart';
import 'products.dart'; // Import the Products class
class ProductDetailsScreen extends StatelessWidget {
final Products product;
ProductDetailsScreen({required this.product});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(product.name),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.network(
product.image,
height: 200,
width: 200,
fit: BoxFit.cover,
),
SizedBox(height: 20),
Text(
'Description: ${product.description}',
style: TextStyle(fontSize: 18),
),
SizedBox(height: 10),
Text(
'Price: \$${product.price.toStringAsFixed(2)}',
style: TextStyle(fontSize: 18),
),
SizedBox(height: 10),
Text(
'Rating: ${product.rating}',
style: TextStyle(fontSize: 18),
),
],
),
),
);
}
}
Update the Main Screen: In the main screen (ECommerceApp
), wrap the ListTile
widget with a GestureDetector
to handle the tap event and navigate to the product details screen when a product is clicked.
class ECommerceApp extends StatelessWidget {
// Your product list remains the same
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('E-Commerce App')),
body: Container(
height: 200,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: productList.length,
itemBuilder: (context, index) {
final product = productList[index];
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailsScreen(product: product),
),
);
},
child: ListTile(
leading: Image.network(product.image),
title: Text(product.name),
subtitle: Text(product.description),
trailing: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('\$${product.price.toStringAsFixed(2)}'),
Text('Rating: ${product.rating}'),
],
),
);
);
},
),
),
);
}
}
Now, when the user taps on a product, the onTap
event triggers, and it navigates the user to the product details screen (ProductDetailsScreen
) with the corresponding product data passed to it.
With this, you have saved time by creating just one screen that handles all the updates on your UI.
I hope this was simple enough.