Build Your Own Responsive Developer Portfolio Website (App) with Flutter
Pradeep Tintali
Posted on November 23, 2021
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
- Live Demo
- Video Explanation
- Creating Project
- Initial Setup
- Directory Setup
- Creating HomePage
- Creating The Navbar Section
- Creating endDrawer
- Creating Components of HomePage
- Creating Project Model
- Source Code
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/
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(),
);
}
}
Directory Setup
Inside the lib
folder create 3 new directory named components
, models
, pages
.
-lib
- components
- models
- pages
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 [
],
),
),
],
),
),
);
}
}
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>();
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,
),
),
),
)
];
}
}
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;
}
}
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,
),
],
and in the Scaffold
of HomePage add the endDrawer
endDrawer: Utils.isMobile(context)
? MyDrawer(
sc: myScrollController,
)
: null,
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)),
);
}
}
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',
),
),
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),
],
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(),
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});
}
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',
),
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!
Posted on November 23, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.