Intro
At Mumble, we're huge Flutter fans. But we're also JAM stack enthusiasts. The cool thing is: we can leverage both these passions and create a Flutter app powered by a headless CMS to make it flexible, dynamic, and future-proof.
In this tutorial, you will build a newsfeed Flutter app using MBurger, a headless CMS. You'll define the data structure for your news through MBurger and use its Flutter SDK to fetch the data and show it in the application. If you're new to Flutter, go to flutter.dev and take a quick tour of what is Flutter and its potential. You won't regret it, I promise you!
Ready to start? Let's do it.
🍔 Create the project on MBurger
The first thing that you have to do is to create a project on MBurger. It's quick, free and preeeetty easy. Go to the MBurger dashboard, login with your credentials (or create a new free account) and then select "Create new project". Since we're starting from scratch, select "Create new project" again.
Give it a cool name to your project and click on "Create". Or give it a boring name and click on "Create" anyway. Who cares? 😅
🔷 Create your Flutter app and install MBurger SDK
Now let's switch to Flutter. Create a new Flutter project; if you don't know how to do it you can follow this tutorial put together by the Flutter team.
Add MBurger to your project adding this to your package's pubspec.yaml file:
dependencies: mburger: ^0.0.1
and install it from the command line:
$ flutter pub get
If you've installed MBurger package correctly, you will be able to import MBurger and set it up.
First we will need an api key to connect the MBurger package to the project you've just created. Head over to the MBurger dashboard and create a new API Key navigating to Control Panel -> API Keys, insert a name for the key and click on "Create".
Now it's time to setup MBurger in your app. Open your main.dart
file and import MBurger adding this line at the top.
import 'package:mburger/mburger.dart';
Then set the api key you've just created like this:
void main() {
// Replace the below string with the API token you get from MBurger
MBManager.shared.apiToken = 'YOUR_API_TOKEN';
runApp(MyApp());
}
📰 News: Create the news block on MBurger
Click on "Add Block" and create a new block. A block is essentially a data model and, in this case, it will represent the main content of your app: a list of news. Give it a name, pick an icon (it's just for internal purpose) and click on "Create".
Now we have to define the structure of the block. We need to tell MBurger that a news block is composed of an image, a title and a some markdown content. Go to the "Content Structure" section of the block, and add a new image element, calling it "Image".
Try it yourself, go ahead! Create a "Text" element and call it "Title", and a "Markdown" element and call it "Content". Does you "Content Structure" page resemble this screenshot below? If so, great! You did it!
Create your first section
Okay, let's create some actual content now. A section is just an instance of the block which contains the data you want to show in your app. To rephrase, the block is the abstract structure of the news while the sections are actual news with pictures, text, etc.
To create your first News, select your News block in the left menu, head over to the "Content List" tab and click on "Add New News". In the page you've just opened, you should see the elements you've picked when building the News block (Title, Image and Content).
Fill each input with some insert the content you wish to see on your home page and click the "Save" button.
Congratulations, you've created your first section! 👏🤩
Create the news section in your app
Okay, so now we've created some content on MBurger, basically exposing some APIs. Now we have to retrieve that section and show it in our app.
Create a new package in your app and call it model
, we will put here all the classes that represents our MBurger sections. Create a new news.dart
file with this code:
import 'package:mburger/elements/mb_markdown_element.dart';
import 'package:mburger/mburger.dart';
class News {
String image;
String title;
String content;
News.fromMBurgerSection(MBSection section) {
section.elements.forEach((key, value) {
if (key == 'image' && value is MBImagesElement) {
image = value.firstImage()?.url;
} else if (key == 'title' && value is MBTextElement) {
title = value.value;
} else if (key == 'content' && value is MBMarkdownElement) {
content = value.value;
}
});
}
}
Have you noticed anything? This class has the same elements we inserted in the section we've created in MBurger. It has an image, a title and a content and will be initialized from an MBSection
object, which is the object that will be returned by the MBurger SDK
. The elements property of the MBSection
is a Map, the keys are the names of the elements that you've created in MBurger, values are instances of MBElement
subclasses. e.g. the title element is a MBTextElement
.
Now that we have created the model object, we need to create the widget that fetches the sections from MBurger
and shows them, create a new package and call it news
, we will put here all the widgets of our newsfeed.
The first Widget that we are going to create is the Scaffold
, the main view with the list of all the news. Create a new dart file and call it news_scaffold.dart
with the following content.
import 'package:flutter/material.dart';
import 'package:mburger/mburger.dart';
import '../model/news.dart';
class NewsScaffold extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('News'),
),
body: FutureBuilder<List<News>>(
future: _news(),
builder: (context, snapshot) => Container(),
),
);
}
Future<List<News>> _news() async {
MBPaginatedResponse<MBSection> homeSections =
await MBManager.shared.getSections(
blockId: BLOCK_ID,
includeElements: true,
);
return homeSections.items.map((s) => News.fromMBurgerSection(s)).toList();
}
}
There are a lot of things going on here, first we have a FutureBuilder
, it takes a Future
as parameter and calls the builder to create the widget, the snapshot parameter of the builder callback there will be the list of news.
The Future parameter of the builder is the other function contained in the Scaffold
, it fetches the sections from MBurger with the SDK and converts them to News
objects using the .map
function. You just need to change BLOCK_ID to the id of your block that you can find under "Content Structure" -> "Block Settings" -> "ID"
Now we have to create the list of news, because at the moment we're returning just a Container()
from the builder function, so our view will look empty. Add this function to your scaffold and change the return of the FutureBuilder
to this: builder: (context, snapshot) => _newsList(context, snapshot.data).
// The list of all the news
Widget _newsList(BuildContext context, List<News> news) {
if (news == null) {
return Container();
}
return ListView.builder(
itemCount: news.length,
itemBuilder: (context, index) => _newsListTile(news[index]),
);
}
// A tile of the list that represents the news, it's a card with the image of the news and its title
Widget _newsListTile(BuildContext context, News news) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
child: ListTile(
contentPadding: const EdgeInsets.all(10),
leading: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(8)),
child: AspectRatio(
aspectRatio: 1,
child: Image.network(
news.image,
fit: BoxFit.cover,
),
),
),
title: Text(news.title),
),
),
);
}
Now we have to show the content of the news when the user taps on the ListTile
, add the onTap parameter to the ListTile and connect it to the following function onTap: () => _showNewsDetail(context, news).
Don't worry if there's an error, it's because we haven't created yet the NewsDetailScaffold
class, we will do it in the next section.
void _showNewsDetail(BuildContext context, News news) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => NewsDetailScaffold(news: news),
),
);
}
News Detail Scaffold
So, let's create the detail of our news, we will create a new Scaffold with all the contents of the news. We will use the flutter_markdown package to render the content of the news so add it to your pub spec.yaml
and run flutter pub get
.
Create a new dart file called news_detail_scaffold.dart
in the news
package with the following content.
It's a very simple Scaffold, the news is passed to it when it's initialized (in our case when the route is created) and it shows the news content in a list, with the image at the top and the markdown content bellow it.
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import '../model/news.dart';
class NewsDetailScaffold extends StatelessWidget {
final News news;
const NewsDetailScaffold({
Key key,
@required this.news,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(news.title),
),
body: ListView(
children: [
AspectRatio(
aspectRatio: 3 / 2,
child: Image.network(
news.image,
fit: BoxFit.cover,
),
),
Padding(
padding: const EdgeInsets.all(20.0),
child: MarkdownBody(
data: news.content,
),
),
],
),
);
}
Where to Go From Here?
If you've reached this paragraph you should have your new newsfeed app and running.
If it's not the case you can use the MBurger template and GitHub repo below as a starting point for your app, you just need to insert the correct data in the constants.dart
file.
Download the sample
Download MBurger Template
You can try to add a lot more functionalities to your app, the only limit is your imagination.
Here are a few examples of what you can built next:
- Add a date property to the news using the availableAt property of MBSection.
- Change the UI of your app which is pretty basics at the moment, add more colors, change the layout of the list tiles, try to build a grid instead of a list.
- Build a Home for your app and use MBurger to edit its content.
- Use BLoC to manage the state of your app. BLoC is one of the most populars approaches when talking about Flutter, try to use it in your newsfeed app. You can find out more at this link.
- Add pull to refresh to the list of news using this package.
- Add pagination using MBPaginationParameter and a ScrollController.
- Catch and manage exceptions that can occur when fetching sections from MBurger, e.g. if your token is wrong or if you request sections with a wrong block id a MBException is raised, try to catch it and show an alert.
- Add push notification functionality using the mbmessages package of MBurger.
Hey, I like this MBurger!
We're super happy to know you've liked MBurger. We're just in the process of launching it and we'd appreciate having you on board as a tester/contributor. Sounds good? Here's what you can do then:
- Join us on Slack here and get involved or
- Reach out and say hi to the team via email or
- Simply keep using MBurger to build awesome stuff! 🚀
Thank you for reading this tutorial. Did you like it? Let us know by clapping or dropping us a comment. Do you like to hunt for bugs? Go ahead, do your worst! 😉
Top comments (1)
Very interesting article!