---
title: "Build a Flutter Wishlist App, Part 1: Introducing Flutter and Building a Basic Wishlist App"
description: "Learn the basics of the Flutter development kit and the Dart programming language and build an app to manage your shopping wishlist."
authors:
  - name: "Michael Bui"
    url: "https://auth0.com/blog/authors/michael-bui/"
date: "Mar 31, 2021"
category: "Developers,Tutorial,Flutter"
tags: ["mobile", "ios", "android"]
url: "https://auth0.com/blog/build-flutter-wishlist-app-with-secure-api-part-1/"
---

# Build a Flutter Wishlist App, Part 1: Introducing Flutter and Building a Basic Wishlist App

In this series of articles, you'll learn how to create a Flutter mobile application that connects to a secured web API. You'll start by building a basic Flutter app that connects to an open API. Once you've written the basic app, you'll learn how to set up the app to use Auth0 to log in. From there, you'll update the app so that it can connect to a secured API. Along the way, you'll learn how to structure your application using a well-known design pattern.

In this article and the one after it, you'll build the basic Flutter app that lets you read, add, edit, and delete items from your wishlist. This basic app won't require the user to log in. You'll add user login and connecting to a secure API in subsequent articles.

[Flutter](https://flutter.dev) is a UI toolkit from Google that has become a popular choice for building cross-platform applications. As it has its own rendering engine, it enables developers to build applications with a more consistent look and feel on multiple platforms more easily, which is helpful when building applications with branded experiences.

By integrating your apps with [Auth0](https://auth0.com), you can control access to resources and information without having to worry about the details of authentication.


## Getting Started

The application featured in this article was written using version 1.22.5 of the Flutter SDK. This was the latest stable version when this tutorial was written. If you need to set up an environment for Flutter development, follow the [guide on the official Flutter website](https://flutter.dev/docs/get-started/install).

> While the current version of the Flutter SDK at the time of writing is 2.0.3, this article's code is compatible with this SDK.

Applications written under older versions of the Flutter SDK will generally work on newer versions unless breaking changes were introduced. If you run into this situation, consult the release notes; they provide more information on breaking changes and how to migrate your code. Alternatively, you can use an older SDK release, which can be found [here](https://flutter.dev/docs/development/tools/sdk/releases).

The application that you'll build will interact with a web API that manages wishlists. In the first part of this article, the app will connect to an API that hasn't been secured. This will make it easier to confirm that the application is able to send and receive information via the API. Later on, the application will connect to a secured API, where you'll use authentication with Auth0.

A template for the API has already been created — all you have to do is generate your own instance. Here's how you do it:

* Open the Glitch project at [**https://glitch.com/edit/#!/wishlist-public-api**](https://glitch.com/edit/#!/wishlist-public-api). This is the main version of the API project. 
* Click on the **Remix to Edit** button in the top-right corner. This will create your own copy of the API project, which you can edit and run. Your project will be assigned a name made of three random words separated by hyphens (e.g., *aardvark-calculator-mousse*), which will appear in the top left corner of the page.
* Click on the **Share** button, which you can find under the project’s name. A pop-up titled **Share your project** will appear. In the **Project links** section at the bottom of the pop-up, make a note of the **Live site** link. This is the root URL of your server, which your application will use to access the API.

> The URL of your server will have the form `https://*project-name-here*.glitch.me`, where ***project-name-here*** is the name of your Glitch project.


### What you'll build

Before we begin, let's take a sneak peek at the Flutter app you'll build:

Here's the screen the user will see when the app launches:

![Landing](https://images.ctfassets.net/23aumh6u8s0i/6W388GgIydhq41u3y4RKHe/80463a65088adb6a01fe7f136b2ed121/01.png)

Here's the screen that displays the wishlist to the user. In this screenshot, it's loading the wishlist items:

![Loading Wishlist](https://images.ctfassets.net/23aumh6u8s0i/4CrkXYdCJpWudFOTmk1UqO/bc2e48a2501bd60303856730f8160757/02.png)

And finally, here's the “Add Item” screen, where the user enters the details for an item to be added to the wishlist:

![Add Item](https://images.ctfassets.net/23aumh6u8s0i/2sNsZX7yGl4GOEA92OghBr/329a4f831df95985995026e9ba03abdb/03.png)

Users will sign into the application via an Auth0 login page. Once signed in, they'll be able to manage items within their wishlist.

Flutter apps are written in the [Dart programming language](https://dart.dev). If you have experience with languages like JavaScript, TypeScript, or C#, you'll find that Dart is pretty similar to them.


## Create the Project

The first step is to create a new project.

Even though there are IDEs that support Flutter development, we’ll use the command line and the commands provided with the SDK to create a new project. If you followed the official documentation to get your environment up and running, the SDK would have been added to your path. This means that you can create a new project by entering this command:

```
flutter create --org com.auth0 flutter_wishlist_app
```

This command creates a new Flutter project named `flutter_wishlist_app`. The `--org` switch specifies that the string “com.auth0” will be used in the [application ID](https://developer.android.com/studio/build/application-id) for the Android version of the app and as the prefix of the [bundle identifier](https://cocoacasts.com/what-are-app-ids-and-bundle-identifiers/) for the iOS version.

After the command is done, you'll have a directory called `flutter_wishlist_app`, where the project's files will be located. Navigate to this directory:

```
cd flutter_wishlist_app
```

The project directory contains all the necessary files for a simple starter app that you can run right away. Connect a device to your computer or [launch an Android emulator](https://developer.android.com/studio/run/emulator#runningemulator) or [iOS Simulator](https://stackoverflow.com/questions/31179706/how-can-i-launch-the-ios-simulator-from-terminal), then run the application via the CLI with the following command:

```
flutter run
```

You should get a basic application that displays a counter that will increase each time you tap on the plus button:

![Counter](https://images.ctfassets.net/23aumh6u8s0i/76bqLdFXAT919COG9uWKma/796d3680b77d9508a88008a1657c0ae9/04.png)

*Note*: there's a difference in the name of the repository, and the name of the application as the Flutter/Dart applications cannot have hyphens in the name.


### Clean Up the Starter App

Using your preferred IDE or the command line, open the project directory. It should have the following layout:

![Project directory structure](https://images.ctfassets.net/23aumh6u8s0i/58kaX8Tw5rr2ze65qN3wBj/fbb843e83625b606235f58812fc4ffee/05.png)

Within the `test` directory is a set of tests that apply to the starter application you just ran. Go ahead and remove all of the files within `test`. You won't be writing tests as part of this tutorial, but it *is* recommended that you do so for production applications.


### Install Dependencies

Our wishlist app makes use of a handful of libraries, packages, and plugins that we'll include in the project as _dependencies_. They are:

* [*http*](https://pub.dartlang.org/packages/http): a library for creating network requests
* [*provider*](https://pub.dartlang.org/packages/provider): a library that can be used for state management
* [*build_runner*](https://pub.dartlang.org/packages/build_runner): a package that is commonly used for code generation
* [*json_serializable*](https://pub.dartlang.org/packages/json_serializable): generates code for dealing with JSON
* [*json_annotation*](https://pub.dartlang.org/packages/json_annotation): a companion for the *json_serializable* package that provides annotations to mark classes that would be involved in handling JSON
* [*url_launcher*](https://pub.dev/packages/url_launcher): a plugin for opening URLs

You specify the dependencies used by a Flutter project in its *pubspec* file. Its name is `pubspec.yaml`, and it's located in the project's root directory.

Open `pubspec.yaml` and replace its `dependencies` section with the following:

```yaml
dependencies:
  flutter:
    sdk: flutter
  http: ^0.12.2
  json_annotation: ^3.1.1
  provider: ^4.3.2+3
  url_launcher: ^5.7.10
```

The `dependencies` section of `pubspec.yaml` tells Flutter which dependencies to include when building the app. The numbers after the `http`, `json_annotation`, `provider`, and `url_launcher` dependencies specify which versions should be used.

Note that you didn't add any lines for the `build_runner` and `json_serializable` dependencies. This is because they’re not part of the application. Instead, they’re tools to automate the process of building the application. As such, they’re classified as [dev dependencies](https://dart.dev/tools/pub/dependencies#dev-dependencies). You specify their use in the `dev_dependencies` section of `pubspec.yaml`.

```yaml
dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^1.10.7
  json_serializable: ^3.5.1
```

Save the changes that you made to `pubspec.yaml`.

Now that you've entered the project's dependencies, it's time to retrieve them from [Pub.dev](https://pub.dev), the package repository for Dart and Flutter applications. The way you'll do this depends on the tools you’re using.

If you’re building the app using an IDE or Visual Studio Code, it may detect that you’ve updated the project's dependencies file and provide you with the options to get the dependencies from Pub.dev. When presented with this option, say yes.

Alternatively, if you’re building the app using a code editor and the command line, get the dependencies by running the following command from within the project's root directory:

```
flutter pub get
```

You now have a basic Flutter app project, complete with the dependencies required for the final project. The next step is to set up the classes for storing and sending data.


## The Data Service and Data Model Classes

### Create the Data Transfer Objects

Our wishlist application will make use of the the following endpoints provided by the web API:

1. A GET endpoint for retrieving the user's wishlist
2. A POST endpoint for adding an item to the wishlist
3. A PUT endpoint for editing an item in the wishlist
4. A DELETE endpoint for deleting an item from the wishlist

The GET endpoint doesn’t need to be provided with any information. The other endpoints, POST, PUT, and DELETE, require the details of the item to be added, edited, or deleted. This requires us to create data model classes to represent wishlist items. We'll put these classes into their own directory, `lib/models/api`.

To create this directory, add a new directory named `models/api` to the project's `lib` directory. 

The first class you'll add to the newly-created `lib/models/api` directory will be `AddItemRequestDTO`, which will represent a request to add an item to the wishlist. The `DTO` suffix denotes that its instances are data transfer objects.

Create a file in `lib/models/api` named `add_item_request_dto.dart` and add the following code to it:

```dart
import 'package:flutter/foundation.dart';
import 'package:json_annotation/json_annotation.dart';
part 'add_item_request_dto.g.dart';
@JsonSerializable()
class AddItemRequestDTO {
  final String name;
  final String description;
  final String url;
  const AddItemRequestDTO({
    @required this.name,
    @required this.description,
    @required this.url,
  });
  factory AddItemRequestDTO.fromJson(Map<String, dynamic> json) =>
      _$AddItemRequestDTOFromJson(json);
  Map<String, dynamic> toJson() => _$AddItemRequestDTOToJson(this);
}
```

The `AddItemRequestDTO` class has the following:

* The properties `name`, `description`, and `url`, which will hold the name, description, and URL of the item to be added to the wishlist
* A constructor that sets those properties
* The methods `fromJson()` and `toJson()`, which convert the class properties to and from JSON

You may have noticed that `fromJson()` references a function named `_$AddItemRequestDTOFromJson()` and `toJson()` references a function named `_$AddItemRequestDTOToJson()`. Neither of these functions exists...yet. The `@JsonSerializable()` annotation, which appears on the line before the start of the class, indicates that the Dart build system should generate those JSON conversion functions and that they should reside in the file specified in the `part` statement: `add_item_request_dto.g.dart`, where the `g` in the filename means "generated".

Since the `_$AddItemRequestDTOFromJson()` and `_$AddItemRequestDTOToJson()` functions and the `add_item_request_dto.g.dart` file haven’t yet been made, your editor or IDE might point out them out as errors. You can ignore them for now.

The next class to add is `EditItemRequestDTO`, which holds the details for a request to edit an item. Create a file called `edit_item_request_dto.dart` in the `lib/models/api` directory with the following code:


```dart
import 'package:flutter/foundation.dart';
import 'package:json_annotation/json_annotation.dart';
part 'edit_item_request_dto.g.dart';
@JsonSerializable()
class EditItemRequestDTO {
  final String name;
  final String description;
  final String url;
  const EditItemRequestDTO({
    @required this.name,
    @required this.description,
    @required this.url,
  });
  factory EditItemRequestDTO.fromJson(Map<String, dynamic> json) =>
      _$EditItemRequestDTOFromJson(json);
  Map<String, dynamic> toJson() => _$EditItemRequestDTOToJson(this);
}
```

Notice that `EditItemRequestDTO` has the same properties and methods as `AddItemRequestDTO`. This is to be expected, as they both deal with wishlist items. Even though they have the same data structure, it's best to keep separate classes for “add” and “edit” requests as they are used to reach different endpoints. This reduces the impact on the code and testing in case one endpoint changes, but the other doesn't.

Just like `AddItemRequestDTO`, `EditItemRequestDTO` uses the `@JsonSerializable()` annotation to specify that the functions to convert its fields to and from JSON should be auto-generated and stored in a file named `edit_item_request_dto.g.dart`.

The last class to add, `ItemDTO`, represents an item in a user's wishlist. Create a file named `item_dto.dart` in the `lib/models/api` directory with the following code:

```dart
import 'package:flutter/foundation.dart';
import 'package:json_annotation/json_annotation.dart';
part 'item_dto.g.dart';
@JsonSerializable()
class ItemDTO {
  final String id;
  final String name;
  final String description;
  final String url;
  const ItemDTO({
    @required this.id,
    @required this.name,
    @required this.description,
    @required this.url,
  });
  factory ItemDTO.fromJson(Map<String, dynamic> json) =>
      _$ItemDTOFromJson(json);
  Map<String, dynamic> toJson() => _$ItemDTOToJson(this);
}
```

`ItemDTO` is similar to `AddItemRequestDTO` and `EditItemRequestDTO`. The only notable difference is that `ItemDTO` contains an additional property: `id`, which holds the ID of the wishlist item.

With that done, you have finished modelling all of the data that will be sent to and from the APIs. Enter the following on the command line to generate the code that allows the `AddItemRequestDTO`, `EditItemRequestDTO`, and `ItemDTO` classes to convert their data to and from JSON:

```
flutter pub run build_runner build
```

Once the command has finished running, the build system will have created all the files with the `g.dart` extension that were referenced in our code, along with the functions they contain. This should resolve the errors that your editor or IDE may have highlighted in `AddItemRequestDTO`, `EditItemRequestDTO`, and `ItemDTO`.

If you are interested in reading more on JSON serialization for Flutter applications, a good place to start is [the *JSON and serialization* page](https://flutter.dev/docs/development/data-and-backend/json) on the Flutter site.


### Create the Domain Models

Although you have created models for the data that is sent to and from the APIs, they may contain more information than needed. This could be solved through the use of domain models that better represent and limit the data that the application actually deals with. Doing so also provides an [anti-corruption layer](https://www.thereformedprogrammer.net/wrapping-your-business-logic-with-anti-corruption-layers-net-core/), as referred to in [domain-driven design](https://martinfowler.com/bliki/DomainDrivenDesign.html). This helps isolate changes that could occur if the schema of the request or response body of an API changes.

Conceptually, users are only concerned with two things: 

1. The wishlist
2. Items within the wishlist

You’ll need to create classes for both of these. 

Items within the wishlist will be represented by instances of the `Item` class. Create a file named `item.dart` in the `lib/models` directory that contains the following code:

```dart
import 'package:flutter/foundation.dart';
class Item {
  final String name;
  final String description;
  final String url;
  final String id;
  const Item({
    @required this.name,
    @required this.description,
    @required this.url,
    this.id,
  });
}
```

The properties should be self-explanatory. Note that `id` is optional; that's because its value will be set only for existing items. When adding new items, the value for `id` will not be specified.

The wishlist, which will contain a collection of items, will be represented by `Wishlist` class. Create a file named `wishlist.dart` in the `lib/models` directory, with the code shown below:

```dart
import 'item.dart';
class Wishlist {
  final List<Item> items;
  Wishlist(this.items);
}
```


### Create a Service to Manage the Wishlist

With the DTOs and domain models in place, you are now ready to create the code that will communicate with the APIs. This will be implemented in the `WishlistService` class, which is responsible for managing the user's wishlist. This class puts the code for making the web requests in a single, centralized place, which encourages reuse and facilitates the writing of tests as well. 

Create a subdirectory of `lib` named `services`. The project will now have a `lib/services` directory. Create a file named `wishlist_service.dart` in `lib/services` and put the following code inside it:

```dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../models/api/add_item_request_dto.dart';
import '../models/api/edit_item_request_dto.dart';
import '../models/api/item_dto.dart';
import '../models/item.dart';
import '../models/wishlist.dart';
class WishlistService {
  static const String itemsApiUrl = 'YOUR_ITEMS_API_URL';
  WishlistService();
  Future<Wishlist> getWishList() async {
    // TODO: send additional info to be able to access protected endpoint
    final http.Response response = await http.get(itemsApiUrl);
    if (response.statusCode == 200) {
      final List<Object> decodedJsonList = jsonDecode(response.body);
      final List<ItemDTO> items = List<ItemDTO>.from(
          decodedJsonList.map((json) => ItemDTO.fromJson(json)));
      return Wishlist(items
          ?.map((ItemDTO itemDTO) => Item(
              id: itemDTO.id,
              name: itemDTO.name,
              description: itemDTO.description,
              url: itemDTO.url))
          ?.toList());
    }
    throw Exception('Could not get the wishlist');
  }
  Future<String> addItem(Item item) async {
    final AddItemRequestDTO addItemRequest = AddItemRequestDTO(
        name: item.name, description: item.description, url: item.url);
    // TODO: send additional info to be able to access protected endpoint
    final http.Response response = await http.post(itemsApiUrl,
        headers: <String, String>{
          'Content-Type': 'application/json',
        },
        body: jsonEncode(addItemRequest.toJson()));
    if (response.statusCode == 201) {
      return response.body;
    }
    throw Exception('Could not add item');
  }
  Future<String> editItem(Item item) async {
    final EditItemRequestDTO editItemRequest = EditItemRequestDTO(
        name: item.name, description: item.description, url: item.url);
    // TODO: send additional info to be able to access protected endpoint
    final http.Response response = await http.put('$itemsApiUrl/${item.id}',
        headers: <String, String>{
          'Content-Type': 'application/json',
        },
        body: jsonEncode(editItemRequest.toJson()));
    if (response.statusCode == 200) {
      return response.body;
    }
    throw Exception('Could not add item');
  }
  Future<void> deleteItem(Item item) async {
    // TODO: send additional info to be able to access protected endpoint
    final http.Response response = await http.delete(
      '$itemsApiUrl/${item.id}',
      headers: <String, String>{
        'Content-Type': 'application/json',
      },
    );
    if (response.statusCode != 204) {
      throw Exception('Could not delete item');
    }
  }
}
```

The `WishlistService` class has the following methods:

* `getWishlist()`: Returns the wishlist. A successful request is indicated by the 200 status code contained within the response, after which the JSON response is deserialized to a list of `ItemDTO` objects. Each `ItemDTO` object is then “mapped” to an instance of the `Item` class. The `Wishlist` would then hold all of the `Item`s in the user's wishlist returned by the method. The method is asynchronous, so its return type is [`Future<Wishlist>`](https://api.dart.dev/stable/2.10.5/dart-async/Future-class.html).
* `addItem()`: Adds the item specified by the `item` parameter to the user’s wishlist. The `item` is mapped to an instance of the `AddItemRequestDTO` class. The `AddItemRequestDTO` object needs to be serialized so that it’s in JSON format; this is done by the `jsonEncode(addItemRequest.toJson()))` portion of the code.
* `editItem()`: Edits the details of the item with the specified ID. The process is similar to the one in `addItem()`, except the `item` is mapped to an instance of the `EditItemRequestDTO` class. 
* `deleteItem()`: Deletes the specified item from the user’s wishlist.

In the code above, the value of the constant `itemApiUrl` field is set to the value `'YOUR_ITEMS_API_URL'`. Replace this with the URL to the `items` endpoint for your Glitch Wishlist API project. Initially, this URL will be of the form **`https://*project-name*.glitch.me/api/wishlist/items`** where ***project-name*** is the name of your copy of the project. For example, if your project's name is *aardvark-calculator-mousse*, you should change the value of `itemApiUrl` to **`https://aardvark-calculator-mousse.glitch.me/api/wishlist/items`**.

Later on, you'll be connecting with a version of the API where all the endpoints are protected. This will need additional code, and `TODO` comments have been left in the code for this purpose.

Note that all the methods in `WishlistService` contain some exception handling code. It's been included for the later version of this project when the mobile app communicates with a secured Wishlist API.

With the data service and data model classes set up, it's time to work on the user interface.


## Setting Up the User Interface

The application you'll be building will consist of three pages:

1. A landing page that is shown when the user needs to log in
2. A page that displays the items in the user’s wishlist
3. A page where the user can enter the name, description, and URL of an item to be added to the wishlist or edit the name, description, or URL of an existing wishlist item

Create a `pages` directory as a subdirectory of the `lib` directory of the project. This will result in a `lib/pages` directory, where the code for each page will reside. 

To allow for the business logic to be decoupled from each page, the application will be built following the [Model-View-ViewModel (MVVM) pattern](https://en.wikipedia.org/wiki/Model–view–viewmodel). Following this pattern enables business logic to be tested in isolation and makes it easier to create reusable[ _widgets_](https://flutter.dev/docs/development/ui/widgets-intro), which is what UI components are called in Flutter. 

With this application, the views are the pages, and the view models represent abstractions of these pages. There will be a corresponding method in the view model for every command that the user can trigger via the user interface (e.g., pressing a button). 

The view model will also contain the data that needs to be presented to the user. For this reason, it’s important for there to be a mechanism for views to subscribe to updates in their corresponding view models. This is where the `provider` package will come in, as you'll see later. 


### The Landing Page

The landing page is where users will log into the application. While the user is in the midst of signing in, it would also be useful to indicate via the user interface that is busy doing so. 

We’ll need a place to store the files for the landing page view and view model. With this in mind, create a `landing` directory under `lib/pages`. 


#### The Landing Page View Model

Create a file for the landing page view model named `landing_view_model.dart` in the newly-created `lib/pages/landing` directory. Add the following code to the file:

```dart
import 'package:flutter/foundation.dart';
class LandingViewModel extends ChangeNotifier {
  bool _signingIn = false;
  bool get signingIn => _signingIn;
  Future<void> signIn() async {
    try {
      _signingIn = true;
      notifyListeners();
      await Future.delayed(Duration(seconds: 3), () {});
      // TODO: handle signing in
    } finally {
      _signingIn = false;
      notifyListeners();
    }
  }
}
```

Classes that extend the Flutter SDK class `ChangeNotifier` can notify objects that register as listeners of any changes. By having `LandingViewModel` extend `ChangeNotifier`, the landing page object can "know" if the user is or is not in the process of signing in, which is indicated by the private instance variable named `_signingIn`. This variable is private in order to prevent external code from being able to change its value directly. Instead, only the `signingIn` getter is publicly visible, making it a read-only property to outside objects.

 The `signIn()` method is invoked when the user makes an attempt to sign in. When that happens, we set the `_signingIn` instance variable to `true` and call `ChangeNotifier`'s `notifyListeners()` method to let the application know that there’s been a change that should be reflected in the user interface.

For now, you'll build a basic version of the mobile app that simulates the process of signing in with an artificial delay created by this line of code:

```dart
await Future.delayed(Duration(seconds: 3), () {});
```

This delay allows you to see how the landing page will look before you implement sign-in functionality.


#### The Landing Page View

With the landing page view model complete, it's time to work on the corresponding view. Create a file named `landing_page.dart` in the `lib/pages/landing` directory and enter the following code into it:

```dart
import 'package:flutter/material.dart';
import 'landing_view_model.dart';
class LandingPage extends StatelessWidget {
  static const String route = '/';
  final LandingViewModel viewModel;
  const LandingPage(
    this.viewModel, {
    Key key,
  }) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Wishlist'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            const Text(
              'Welcome to your wishlist.',
              textAlign: TextAlign.center,
            ),
            const Text(
              'Sign in to get started.',
              textAlign: TextAlign.center,
            ),
            if (viewModel.signingIn) ...const <Widget>[
              SizedBox(
                height: 32,
              ),
              Center(child: CircularProgressIndicator()),
            ],
            const Expanded(
              child: SizedBox(
                height: 32,
              ),
            ),
            RaisedButton(
              onPressed: viewModel.signingIn
                  ? null
                  : () async {
                      await signIn(context);
                    },
              child: const Text('Sign in with Auth0'),
            ),
          ],
        ),
      ),
    );
  }
  Future<void> signIn(BuildContext context) async {
    await viewModel.signIn();
    // TODO: navigate to wishlist page
  }
}
```

With its view and view model defined, the landing page can now be displayed. In order to do this, you'll need to modify the app's [entry point](https://en.wikipedia.org/wiki/Entry_point), `main.dart`. It's located in the `lib` directory.

Replace the code inside `main.dart` with the following:

```dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';
import 'pages/landing/landing_page.dart';
import 'pages/landing/landing_view_model.dart';
Future<void> main() async {
  // TODO: change initial route based on if user signed in before
  final String initialRoute = LandingPage.route;
  runApp(
    MultiProvider(
      providers: <SingleChildWidget>[
        // TODO: register other dependencies
        ChangeNotifierProvider<LandingViewModel>(
          create: (BuildContext context) => LandingViewModel(),
        ),
      ],
      child: MyApp(initialRoute),
    ),
  );
}
class MyApp extends StatelessWidget {
  final String initialRoute;
  const MyApp(
    this.initialRoute, {
    Key key,
  }) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Wishlist',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      initialRoute: initialRoute,
      onGenerateRoute: (RouteSettings settings) {
        switch (settings.name) {
          case LandingPage.route:
            return MaterialPageRoute(
              builder: (_) => Consumer<LandingViewModel>(
                builder: (_, LandingViewModel viewModel, __) =>
                    LandingPage(viewModel),
              ),
            );
        }
        return null;
      },
    );
  }
}
```

Let's take a look at the code you've just added. 

Within the `main()` function is an `initialRoute` variable that represents the _route_ to present to the user when they first run the application. A route can be thought of as a destination that presents a widget (once again, widgets are UI components).

To register the dependencies used by the code, this is done by using the `provider` package. Applications will typically have multiple dependencies that need to be registered. This is represented here via the use of the `MultiProvider` widget that wraps around the `MyApp` class that represents the application itself. In other words, `MyApp` is the child of the `MultiProvider` widget. For now, you're registering one dependency, and that is the view model for the landing page. By using the `ChangeNotifierProvider` from the `provider` package, allows a `ChangeNotifier` instance (the `LandingViewModel`) to be provided to descendent widgets.

The `MyApp` class extends the `StatelessWidget` class, which defines a user interface by building a collection of other widgets that provide a more detailed description of the user interface. In this case, `MyApp` uses `StatelessWidget`'s `build()` method to return a `MaterialApp` widget, which specifies that the app should be built using Google's [Material Design](https://material.io/design/introduction).

In instantiating the `MaterialApp` widget, the `initialRoute` parameter specifies which widget should be shown when the app launches. This will be either the landing page or the wishlist page. The `routes` parameter is a `Map` that essentially is a lookup table that connects named routes to widgets, each of which represents a page. 

When the app starts up, the value passed in the `MaterialApp` constructor’s `initialRoute` parameter is set to `LandingPage.route`. The app then looks up this route in the table provided in the `MaterialApp` constructor’s `routes` parameter to find the corresponding value, which is a function that builds the landing page and displays it via an animated transition.

You may have noticed that in the `routes` table, the page corresponding to `LandingPage.route` is wrapped by a `Consumer` class. This is a widget from the `provider` package, and using it ensures that the landing page has access to its associated view model, `LandingViewModel`. The `Consumer` widget also helps to re-render the landing page via the `builder` property in response to change notifications being sent by its view model. 

Structuring the code to manage the dependencies this way provides another benefit. Should a page need to be presented to a user again, a new instance of the associated view model is created. This prevents the application from showing stale data from the last time that page was shown.

You should now be able to run your application. The landing page should look like this:

![Landing](https://images.ctfassets.net/23aumh6u8s0i/6WmnAZuAyeCLDHbPA6tztH/6a73720deb71e24fdffb17fbc9e429c4/06.png)

You can see that there’s an application bar with **Wishlist** as the title. A welcome message and button docked to the bottom of the screen can be used to sign in. Pressing the button calls the method in the view model that displays a circular progress indicator for 3 seconds. 

Right now, you have an app that displays a single page and has the necessary underlying models to manage a wishlist. In the next article, you’ll put those models to work and give the user the ability to read their wishlist, as well as add, edit, and delete wishlist items.