DEV Community

Cover image for Build Your Own Responsive Developer Portfolio Website (App) with Flutter
Pradeep Tintali
Pradeep Tintali

Posted on

Build Your Own Responsive Developer Portfolio Website (App) with Flutter

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

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

If you are so excited to see what we'll build like me, you can see the live demo of app Here. This demo of web version is deployed using html renderer.
Live Demo

Video Explanation

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

Creating Project

Supposing flutter is installed and initial setup is done in your system.
If you have't installed then you can get it from here
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

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/

Now load images in pubspec.yaml file

assets:
    - assets/images/
Enter fullscreen mode Exit fullscreen mode

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

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(),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Directory Setup

Inside the lib folder create 3 new directory named components, models, pages.

-lib
   - components
   - models
   - pages
Enter fullscreen mode Exit fullscreen mode

Creating HomePage

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.

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 [

                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

final ScrollController myScrollController = ScrollController();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
Enter fullscreen mode Exit fullscreen mode

add the _scaffoldKey in the Scaffold widget and myScrollController in the SingleChildScrollView.

Creating The Navbar Section

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.

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,
            ),
          ),
        ),
      )
    ];
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

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,
                ),
        ],
Enter fullscreen mode Exit fullscreen mode

and in the Scaffold of HomePage add the endDrawer

endDrawer: Utils.isMobile(context)
          ? MyDrawer(
              sc: myScrollController,
            )
          : null,
Enter fullscreen mode Exit fullscreen mode

now lets create our MyDrawer widget

Creating endDrawer

Inside the components create a new file my_drawer.dart and write the following code

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)),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

 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',
        ),
      ),
Enter fullscreen mode Exit fullscreen mode

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

Now in the HomePage inside the children of column add the remaining components.

children: const [
Header1(),
MoreAboutMe(),
SizedBox(height: 50),
TopSkills(),
SizedBox(height: 50),
RecentProjects(),
SizedBox(height: 50),
ContactForm(),
SizedBox(height: 50),
Footer(),
SizedBox(height: 20),
],
Enter fullscreen mode Exit fullscreen mode

You can get the code of these components from the Here.

Now the final thing in the bottom of children of Stack that we used in HomePage add the following component.

const SocialIconsBar(),
Enter fullscreen mode Exit fullscreen mode

Creating Project Model

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.

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});
}
Enter fullscreen mode Exit fullscreen mode

Create a new file inside the lib called constants.dart and create some ProjectModels/Demo Projects.

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',
    ),
Enter fullscreen mode Exit fullscreen mode

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

Source Code

You can see/download the source code from github.
Repo Link

Thanks!

Discussion (0)