Have you tried headless Flutter? It takes 10 minutes and it will blow your mind!
Tags: flutter, dart, beginners
Published: 2020-12-01 14:28:26

## 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](https://web.mburger.cloud), a headless CMS. You'll define the data structure for your news through MBurger and use its [Flutter SDK](https://pub.dev/packages/mburger) to fetch the data and show it in the application. If you're new to Flutter, go to [flutter.dev](https://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.

<br>
## 🍔 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](https://mburger.cloud), 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? 😅
![](https://cdn.mburger.cloud/184589/Create-the-project-on-MBurger.png)

<br>
# 🔷 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](https://flutter.dev/docs/get-started/codelab) put together by the Flutter team.

Add MBurger to your project adding this to your package's pubspec.yaml file:
``` yaml
dependencies: mburger: ^0.0.1
```
and install it from the command line:
``` bash
$ 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".
![](https://cdn.mburger.cloud/184661/MBurger-API-Key.png)

Now it's time to setup MBurger in your app. Open your `main.dart` file and import MBurger adding this line at the top.
``` dart
import 'package:mburger/mburger.dart';
```
Then set the api key you've just created like this:
``` dart
void main() {
  // Replace the below string with the API token you get from MBurger
	MBManager.shared.apiToken = 'YOUR_API_TOKEN';
	runApp(MyApp());
}
```

<br>
## 📰 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". 
![](https://cdn.mburger.cloud/184587/Create-the-news-block-on-MBurger.png)

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".
![](https://cdn.mburger.cloud/184685/Define-element-type-on-MBurger.png)
![](https://cdn.mburger.cloud/184592/Define-setting-on-MBurger.png)

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!
![](https://cdn.mburger.cloud/184709/Order-field-on-MBurger.png)

<br>
## 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! 👏🤩
![](https://cdn.mburger.cloud/184604/Insert-content-on-MBurger.png)

<br>
## 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:
``` dart
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. 
``` dart
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).`
``` dart
  // 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.
``` dart
  void _showNewsDetail(BuildContext context, News news) {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) => NewsDetailScaffold(news: news),
      ),
    );
  }
```

<br>
## 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.

``` dart
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,
            ),
          ),
        ],
      ),
    );
  }
```

<br>
## 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](https://github.com/Mumble-SRL/MBurger-Flutter-News)
#### [Download MBurger Template](https://cdn.mburger.cloud/184782/mburger-news_2020-11-20T09_53_29%2B00_00.json)

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](https://pub.dev/documentation/mburger/latest/mb_section/MBSection/availableAt.html) 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](https://flutter.dev/docs/development/data-and-backend/state-mgmt/options#bloc--rx). 
* Add pull to refresh to the list of news using [this package.](https://pub.dev/packages/pull_to_refresh) 
* Add pagination using [MBPaginationParameter](https://pub.dev/documentation/mburger/latest/parameters_mb_pagination_parameter/MBPaginationParameter-class.html) and a [ScrollController](https://api.flutter.dev/flutter/widgets/ScrollController-class.html).
* 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](https://pub.dev/documentation/mburger/latest/mb_exception/MBException-class.html) is raised, try to catch it and show an alert.
* Add push notification functionality using the [mbmessages](https://pub.dev/packages/mbmessages) package of MBurger.

<br>
## 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](https://join.slack.com/t/mburgerworkspace/shared_invite/zt-bgrx0y2u-7YGQGnSyIs2Q~T2Atfmbog) and get involved or
* Reach out and say hi to the team via [email](mailto:hello@mburger.cloud) 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! 😉