Build Your Own Responsive Developer Portfolio Website (App) with Flutter
Tags: flutter, tutorial, portfolio
Published: 2021-11-23 15:54:00

*The best way of learning something is by doing.*
And if you have to build your own developer portfolio then why not build it by yourself!

In this tutorial I'll tell you step by step how to build responsive developer portfolio website/app with flutter. By following this tutorial you will learn - 

- How to build responsive UI with flutter.
- How to use `ScrollController` and scroll programmatically.

## Table of Content
* [What we'll build](#What-we'll-build)
* [Live Demo](#Live-Demo)
* [Video Explanation](#Video-Explanation)
* [Creating Project](#Creating-Project)
* [Initial Setup](#Initial-Setup)
* [Directory Setup](#Directory-Setup)
* [Creating HomePage](#Creating-HomePage)
* [Creating The Navbar Section](#Creating-The-Navbar-Section)
* [Creating endDrawer](#Creating-endDrawer)
* [Creating Components of HomePage](#Creating-Components-of-HomePage)
* [Creating Project Model](#Creating-Project-Model)
* [Source Code](#Source-Code)

## What we'll build <a name="What-we'll-build"></a>
We are going to build a portfolio app with mainly 4 sections `about`, `skills`, `projects` and `contact`. For making our app responsive we'll use media query.

## Live Demo <a name="Live-Demo"></a>
If you are so excited to see what we'll build like  me, you can see the live demo of app [Here](https://sad-blackwell-98fd2c.netlify.app/). This demo of web version is deployed using html renderer.
[Live Demo](https://sad-blackwell-98fd2c.netlify.app)

## Video Explanation <a name="Video-Explanation"></a>
At the time of writing this post. It was getting too long so I added this video explanation section.
If you want to watch video tutorial of this post you can watch it from here.
[Video Tutorial](https://www.youtube.com/watch?v=I_pb-WBEJRA)

## Creating Project <a name="Creating-Project"></a>
Supposing flutter is installed and initial setup is done in your system.
If you have't installed then you can get it from [here](https://docs.flutter.dev/get-started/install)
Create a new flutter project by using command palette in VS Code or from android studio or by typing the following command in terminal
`flutter create <YOUR PROJECT NAME>`

## Initial Setup <a name="Initial-Setup"></a>
Now careate a new folder in your project `assets`
Inside the `assets` folder create a subfolder `images`

Add an image that you want to show on your portfolio, in your images folder.
Your image should be placed like this
assets/images/<YOUR IMAGE>

Now load images in `pubspec.yaml` file
```yaml
assets:
    - assets/images/
```

Clear everything from `main.dart` file and write the following code.

```dart
import 'package:flutter/material.dart';
import 'package:portfolio_app/pages/home_page.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner:false,
      home: HomePage(),
    );
  }
}
```

## Directory Setup <a name="Directory-Setup"></a>
Inside the `lib` folder create 3 new directory named `components`, `models`, `pages`.

```yaml
-lib
   - components
   - models
   - pages
```

## Creating HomePage <a name="Creating-HomePage"></a>
As you can see in the above code we have used `HomePage` in the home of `MaterialApp` lets create it.
Inside the pages directory create a new file `homepage.dart`.
Create a stateless widget HomePage in it like below.

```dart
import 'package:flutter/material.dart';

class HomePage extends StatelessWidget {
  HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      //Add Key
      appBar: AppBar(
        title: const Text(
          'JANE DOE',
          style: TextStyle(
            fontSize: 22,
            color: Color(0xFF4756DF),
            fontWeight: FontWeight.bold,
          ),
        ),
        backgroundColor: Colors.white,
        elevation: 2,
      ),
      body: SafeArea(
        child: Stack(
          children: [
            SingleChildScrollView(
            //Add Scroll Controller
              child: Column(
                children: const [
                  
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}
```
Now we need to create a `GlobalKey` and a `ScrollController`.

**GlobalKey** - for opening and closing the end drawer that we'll use later.
**ScrollController** - for scrolling to a specific section of page.

```dart
final ScrollController myScrollController = ScrollController();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
```
add the `_scaffoldKey` in the Scaffold widget and `myScrollController` in the SingleChildScrollView.

## Creating The Navbar Section <a name="Creating-The-Navbar-Section"></a>
Because we have to create a responsive UI we'll show the navbar inside the `endDrawer` of Scaffold on small screen and inside the `actions` of AppBar on large screen.

Inside the components directory caret a new file `home_page_actions.dart` and write the following code.

```dart
import 'package:flutter/material.dart';
import 'package:portfolio_app/utils.dart';

class HomePageActions extends StatelessWidget {
  final ScrollController sc;
  const HomePageActions({Key? key, required this.sc}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    bool isMobile = Utils.isMobile(context);
    return isMobile
        ? Column(
            children: getWidgets(sc, context, isMobile: true),
          )
        : Row(
            children: getWidgets(sc, context),
          );
  }

  List<Widget> getWidgets(ScrollController sc, BuildContext context,
      {bool isMobile = false}) {
    int scrollDur = isMobile ? 800 : 500;
    return [
      Padding(
        padding:
            EdgeInsets.symmetric(horizontal: 10, vertical: isMobile ? 20 : 0),
        child: TextButton(
          onPressed: () {
            if (isMobile) Navigator.pop(context);
            sc.animateTo(
              200,
              duration: Duration(milliseconds: scrollDur),
              curve: Curves.easeIn,
            );
          },
          child: const Text(
            'About',
            style: TextStyle(
              fontSize: 17,
              color: Color(0xFf4756DF),
              fontWeight: FontWeight.w600,
            ),
          ),
        ),
      ),
      Padding(
        padding:
            EdgeInsets.symmetric(horizontal: 10, vertical: isMobile ? 20 : 0),
        child: TextButton(
          onPressed: () {
            if (isMobile) Navigator.pop(context);
            sc.animateTo(
              isMobile ? 1100 : 800,
              duration: Duration(milliseconds: scrollDur),
              curve: Curves.easeIn,
            );
          },
          child: const Text(
            'Skills',
            style: TextStyle(
              fontSize: 17,
              color: Color(0xFf4756DF),
              fontWeight: FontWeight.w600,
            ),
          ),
        ),
      ),
      Padding(
        padding:
            EdgeInsets.symmetric(horizontal: 10, vertical: isMobile ? 20 : 0),
        child: TextButton(
          onPressed: () {
            if (isMobile) Navigator.pop(context);
            sc.animateTo(
              1400,
              duration: Duration(milliseconds: scrollDur),
              curve: Curves.easeIn,
            );
          },
          child: const Text(
            'Projects',
            style: TextStyle(
              fontSize: 17,
              color: Color(0xFf4756DF),
              fontWeight: FontWeight.w600,
            ),
          ),
        ),
      ),
      Padding(
        padding:
            EdgeInsets.symmetric(horizontal: 10, vertical: isMobile ? 20 : 0),
        child: TextButton(
          onPressed: () {
            if (isMobile) Navigator.pop(context);
            sc.animateTo(
              isMobile ? 2600 : 1900,
              duration: Duration(milliseconds: scrollDur),
              curve: Curves.easeIn,
            );
          },
          child: const Text(
            'Contact',
            style: TextStyle(
              fontSize: 17,
              color: Color(0xFf4756DF),
              fontWeight: FontWeight.w600,
            ),
          ),
        ),
      )
    ];
  }
}
```

There are lots of things in the above code but we'll see only the important one.

As you can see we have used `Utils` class so lets create it.

Inside the lib create a new directory called `utils.dart` and write the following code.

```dart
import 'package:flutter/cupertino.dart';

class Utils {
  static bool isMobile(BuildContext context) {
    return MediaQuery.of(context).size.width <= 600;
  }

  static double mdqh(BuildContext context) {
    return MediaQuery.of(context).size.height;
  }

  static double mdqw(BuildContext context) {
    return MediaQuery.of(context).size.width;
  }
}
```

the `isMobile` method returns `true` if the screen width of device is less than or equal to 600 pixel, you can decide your own logic for that.

`mdqh` method returns the height of screen
`mdqw` method return the width of screen

These utilities will help us to make UI responsive.

Now go to HomePage and inside the `AppBar` of Scaffold add the following code. 

```dart
actions: [
          Utils.isMobile(context)
              ? Padding(
                  padding: const EdgeInsets.only(right: 10),
                  child: IconButton(
                    icon: const Icon(
                      Icons.menu,
                      color: Color(0xFf4756DF),
                      size: 30,
                    ),
                    onPressed: () {
                      _scaffoldKey.currentState!.openEndDrawer();
                    },
                  ),
                )
              : HomePageActions(
                  sc: myScrollController,
                ),
        ],
```

and in the `Scaffold` of HomePage add the `endDrawer`

```dart
endDrawer: Utils.isMobile(context)
          ? MyDrawer(
              sc: myScrollController,
            )
          : null,
```
now lets create our `MyDrawer` widget

## Creating endDrawer <a name="Creating-endDrawer"></a>
Inside the components create a new file `my_drawer.dart` and write the following code

```dart
import 'package:flutter/material.dart';
import 'package:portfolio_app/components/home_page_actions.dart';

class MyDrawer extends StatelessWidget {
  final ScrollController sc;
  const MyDrawer({Key? key, required this.sc}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.transparent,
        elevation: 0.0,
        leading: IconButton(
          onPressed: () {
            Navigator.pop(context);
          },
          icon: const Icon(
            Icons.close,
            color: Color(0xFF4756DF),
          ),
        ),
      ),
      body: Center(child: HomePageActions(sc: sc)),
    );
  }
}
```
You can see we are reusing the `HomePageActions` again here and it will be visible only on the small screen, remember the code that we wrote in it.

Now inside the HomePage `Scaffold` add the floating action button that will help to scroll at top. 

```dart
 floatingActionButton: FloatingActionButton(
        mini: Utils.isMobile(context) ? true : false,
        onPressed: () {
          myScrollController.animateTo(
            0,
            duration: const Duration(milliseconds: 800),
            curve: Curves.easeIn,
          );
        },
        child: Image.network(
          'https://eager-williams-af0d00.netlify.app/assets/icons/icons8-upward-arrow.gif',
        ),
      ),
```
`animateTo` method of ScrollController will scroll the page to the specified position will nice animation you can define the `duration` and `curve` for the animation.

The same method we are using in the HomePageActions buttons to scroll at a specific section using a calculated height.

_If I write the complete code the article will be too long and boring. So lets quickly complete our project. You can find the source code in the last section_

## Creating Components of HomePage <a name="Creating-Components-of-HomePage"></a>
Now in the HomePage inside the `children` of column add the remaining components.

```dart
children: const [
Header1(),
MoreAboutMe(),
SizedBox(height: 50),
TopSkills(),
SizedBox(height: 50),
RecentProjects(),
SizedBox(height: 50),
ContactForm(),
SizedBox(height: 50),
Footer(),
SizedBox(height: 20),
],
```
You can get the code of these components from the [Here](https://github.com/pktintali/flutter-portfolio/tree/main/lib/components).

Now the final thing in the bottom of `children` of `Stack` that we used in HomePage add the following component.
```dart
const SocialIconsBar(),
```

## Creating Project Model <a name="Creating-Project-Model"></a>
In the `RecentProjects()` component we need Projectmodel so
in the models directory create a new file called `project_model.dart` and write the following code.

```dart
lass ProjectModel {
  final String imgURL;
  final String projectName;
  final String? shortDescription;
  final String actionLink;

  ProjectModel(
      {required this.imgURL, required this.projectName, this.shortDescription, required this.actionLink});
}
```
Create a new file inside the lib called `constants.dart` and create some ProjectModels/Demo Projects.

```dart
import 'package:portfolio_app/models/project_model.dart';

class Constants {
  static final List<ProjectModel> projects = [
    ProjectModel(
      actionLink: '#',
      imgURL:
          'https://eager-williams-af0d00.netlify.app/assets/images/expenseTracker.png',
      projectName: 'Expense Tracker',
    ),
    ProjectModel(
      actionLink: '#',
      imgURL:
          'https://eager-williams-af0d00.netlify.app/assets/images/netflixClone.png',
      projectName: 'Netflix Clone',
    ),
    ProjectModel(
      actionLink: '#',
      imgURL:
          'https://eager-williams-af0d00.netlify.app/assets/images/greenyEarth.png',
      projectName: 'Greeny Earth',
    ),
```

And we have successfully created our responsive developer portfolio with flutter 🎉

## Source Code <a name="Source-Code"></a>
You can see/download the source code from github.
[Repo Link](https://github.com/pktintali/flutter-portfolio)

Thanks!