In this article, we’ll guide you through building a Custom Linktree-style Portfolio using Flutter Web and deploying it on Firebase. A Linktree is a powerful tool to showcase your online presence - social media, projects, or professional links - all in one sleek, responsive page. With Flutter Web, you can design a visually stunning portfolio that works seamlessly across devices, while Firebase makes deployment and updates effortless.
Step-by-Step Implementation
Step 1: Create a new Flutter Application
Create a new Flutter application using the command Prompt. To create a new app, write the following command and run it.
flutter create app_name
To know more about it refer this article: Creating a Simple Application in Flutter
Step 2: Adding the Dependency
To add the dependency to the pubspec.yaml file, add url_launcher as a dependency in the dependencies part of the pubspec.yaml file, as shown below:
Dart
dependencies:
flutter:
sdk: flutter
url_launcher: ^6.3.1
Now, run the command below in the terminal.
flutter pub get
Or
Run the command below in the terminal.
flutter pub add url_launcher
Step 3: Import dependencies
To use libraries, import all of them in the respective .dart file.
import 'package:url_launcher/url_launcher.dart';
- Collect Assets
After that, we need to collect all the necessary images and icons that will be used in our Flutter app. You can find all the required social media icons in the GitHub repository if you wish to use the same ones as me. Additionally, you will need to upload a single image of yourself to be displayed in the app.
You should also make the necessary changes to the project's 'pubspec.yaml' file to ensure these assets are recognizable. You need to add or uncomment the lines below:
assets:
- assets/
Make sure to use proper indentation.
Step 4: Start Coding
Now we will start coding our web app. In the main.dart file the imports necessary dependencies, including the HomePage screen. The MyApp class, as the app's root, configures the app's title and theme. The theme's primary color is set to deep purple. The app's structure is defined using the MaterialApp widget, with a title and theme. The home screen is set to MyHomePage. This code showcases the fundamental setup of a Flutter web app, with a customized theme and a designated home page.
main.dart:
Dart
import 'package:flutter/material.dart';
import 'Screens/home_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Your Name',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(),
);
}
}
Now we will code the home page of our application. First, we'll create a 'MyHomePage' widget, which is a 'StatefulWidget'. Inside its state, we define the layout and components of our home page.
There are some points to understand for this application:
- For responsive design, we detect whether the screen width is greater than 800 pixels to determine if it's a desktop layout.
- The 'Scaffold' widget serves as the main container. Within it, we place a 'Container' that covers the entire screen with a gradient background.
- Inside the 'Container', we use a 'Stack' to layer the content. The 'Stack' contains a 'Column' that holds the upper and lower sections of the page.
- The upper section features an 'Expanded' widget for flexible sizing. It contains a 'Center' widget with a 'Column' of elements: a circular avatar, the developer's name, and a short bio. These elements are styled using various font sizes and weights.
- The lower section is also wrapped in an 'Expanded' widget. It's a 'Container' with a colored background, rounded top corners, and a vertical layout. Inside it, we have a 'SingleChildScrollView' that contains a 'Column' of social media links and icons. Depending on the screen size, either a 'RowView' or 'ColumnView' layout is used.
- The 'ColumnView' function returns a vertical arrangement of social media icons, with spacing adjusted for responsiveness.
- The 'RowView' function returns a horizontal arrangement of social media icons divided into two columns. Each column holds various social media links.
- The 'geeksforgeeks' is a placeholder for functions or widgets that represent individual social media icons.
- Finally, the 'Footer' widget is placed at the bottom of the page, offering additional information or navigation.
This comprehensive code snippet constructs a responsive Flutter web page with a visually appealing layout and interactive social media links, tailored to desktop and mobile devices.
home_page.dart:
Dart
import 'package:flutter/material.dart';
import 'package:web_links/widgets/animated_container.dart';
import 'package:web_links/widgets/footer.dart';
import 'package:web_links/widgets/socialCards.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
bool isDesktop = MediaQuery.of(context).size.width > 800;
return Scaffold(
body: Container(
// 100% of height
height: MediaQuery.of(context).size.height,
// 100% of width
width: MediaQuery.of(context).size.width,
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xff9dc9e9), Color(0xff2059ff)],
),
),
child: Stack(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 20.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
CircleAvatar(
radius: 80,
backgroundImage: AssetImage('assets/ankit.png'),
),
SizedBox(
height: 5,
),
Text(
// Your Name
'Ankit Kumar',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 28,
),
),
SizedBox(
height: 5,
),
Text(
// Your short bio (optional)
'Passionate Flutter Developer',
style: TextStyle(
fontWeight: FontWeight.w400,
fontStyle: FontStyle.italic,
fontSize: 20,
),
),
],
),
),
),
),
const SizedBox(
height: 10,
),
Expanded(
child: Container(
// height: 490,
width: MediaQuery.of(context).size.width,
decoration: const BoxDecoration(
color: Color(0xff171430),
shape: BoxShape.rectangle,
borderRadius: BorderRadius.vertical(
top: Radius.circular(50.0),
),
),
child: SingleChildScrollView(
child: Column(
children: [
Center(
child: isDesktop ? RowView() : ColumnView(),
),
Footer(),
],
),
),
),
),
],
),
],
),
),
);
}
Column ColumnView() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: MediaQuery.of(context).size.height * 0.06),
codewars(),
geeksforgeeks(),
github(),
gmail(),
hashnode(),
instagram(),
playstore(),
portfolio(),
resume(),
twitter(),
whatsapp(),
youtube(),
SizedBox(height: MediaQuery.of(context).size.height * 0.05),
],
);
}
Row RowView() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.05,
),
codewars(),
geeksforgeeks(),
github(),
gmail(),
hashnode(),
instagram(),
],
),
SizedBox(width: MediaQuery.of(context).size.width * 0.05),
Column(
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.05,
),
playstore(),
portfolio(),
resume(),
twitter(),
whatsapp(),
youtube(),
],
),
// Instagram
],
);
}
}
The following functions efficiently create social media cards using the 'CustomAnimatedContainer' widget :
- Resume: This orange card features a resume image with a direct link to the Google Drive resume.
- Portfolio: A deep purple card showcases a portfolio image, linking seamlessly to the developer's website.
- Twitter: The light blue card prominently displays a Twitter image and connects to the developer's Twitter account.
- GitHub: This dark gray card represents GitHub, providing a link to the developer's repository.
- LinkedIn: A blue card features a LinkedIn image, linking directly to the developer's LinkedIn page.
- Instagram: The pink card highlights Instagram, linking to the developer's account with ease.
- YouTube: A striking red card showcases the YouTube branding, linking straight to the developer's channel.
- Gmail: A red accent card features a Gmail image, linking to compose an email effortlessly.
- GeeksForGeeks: This green card highlights GeeksForGeeks, linking to the developer's articles effectively.
- PlayStore: A light blue accent card showcases the Play Store image, linking to the developer's app with precision.
- WhatsApp: The green card prominently features WhatsApp branding, linking directly to the developer's contact.
These functions create impactful, reusable, and visually appealing social media cards for a Flutter web app.
socialCards.dart:
Dart
import 'package:flutter/material.dart';
import 'package:geeks_for_geeks/animated_container.dart';
// Resume container with a button
// linking to the latest resume
CustomAnimatedContainer resume() {
return CustomAnimatedContainer(
// Yellow color theme
containerColor: Color.fromARGB(255, 245, 176, 3),
borderColor: Color.fromARGB(255, 245, 176, 3),
imagePath: 'assets/resume.png',
text: 'Latest Resume',
// Add your resume link here
linkUrl: '',
);
}
// Portfolio container with a button
// linking to the portfolio
CustomAnimatedContainer portfolio() {
return CustomAnimatedContainer(
// Purple theme for portfolio
containerColor: Colors.deepPurpleAccent,
borderColor: Colors.deepPurpleAccent,
imagePath: 'assets/portfolio.png',
text: 'Portfolio',
// Add your portfolio link here
linkUrl: '',
);
}
// Twitter container with a button
// linking to Twitter profile
CustomAnimatedContainer twitter() {
return CustomAnimatedContainer(
// Blue theme for Twitter
containerColor: Colors.lightBlue,
borderColor: Colors.lightBlue,
imagePath: 'assets/x.png',
text: 'Twitter',
// Add your Twitter profile link here
linkUrl: '',
);
}
// GitHub container with a button
// linking to GitHub profile
CustomAnimatedContainer github() {
return CustomAnimatedContainer(
// Dark theme for GitHub
containerColor: Colors.black38,
borderColor: Colors.black38,
imagePath: 'assets/git.png',
// Fixed typo from 'GtiHub' to 'GitHub'
text: 'GitHub',
// Add your GitHub profile link here
linkUrl: '',
);
}
// LinkedIn container with a button
// linking to LinkedIn profile
CustomAnimatedContainer linkedin() {
return CustomAnimatedContainer(
// Blue theme for LinkedIn
containerColor: Colors.blueAccent,
borderColor: Colors.blueAccent,
imagePath: 'assets/linkedin.png',
text: 'LinkedIn',
// Add your LinkedIn profile link here
linkUrl: '',
);
}
// Instagram container with a button
// linking to Instagram profile
CustomAnimatedContainer instagram() {
return CustomAnimatedContainer(
// Pink theme for Instagram
containerColor: Colors.pink,
borderColor: Colors.pink,
imagePath: 'assets/insta.png',
text: 'Instagram',
// Add your Instagram profile link here
linkUrl: '',
);
}
// YouTube container with a button
// linking to YouTube channel
CustomAnimatedContainer youtube() {
return CustomAnimatedContainer(
// Red theme for YouTube
containerColor: Colors.red,
borderColor: Colors.red,
imagePath: 'assets/youtube.png',
text: 'YouTube',
// Add your YouTube channel link here
linkUrl: '',
);
}
// Gmail container with a button
// linking to an email address
CustomAnimatedContainer gmail() {
return CustomAnimatedContainer(
// Red theme for Gmail
containerColor: Colors.redAccent,
borderColor: Colors.redAccent,
imagePath: 'assets/mail.png',
text: 'Gmail',
// Add your email address here
linkUrl: 'mailto:[email protected]',
);
}
// GeeksForGeeks container with a button
// linking to GeeksForGeeks website
CustomAnimatedContainer geeksforgeeks() {
return CustomAnimatedContainer(
// Green theme for GeeksForGeeks
containerColor: Colors.green,
borderColor: Colors.green,
imagePath: 'assets/gfg.png',
text: 'GeeksForGeeks',
// Add your GFG profile link here
linkUrl: 'https://www.geeksforgeeks.org',
);
}
// PlayStore container with a button
// linking to Google Play Store
CustomAnimatedContainer playstore() {
return CustomAnimatedContainer(
// Blue theme for Play Store
containerColor: Colors.lightBlueAccent,
borderColor: Colors.lightBlueAccent,
imagePath: 'assets/playstore.png',
text: 'PlayStore',
// Add your Play Store app link here
linkUrl: '',
);
}
// WhatsApp container with a button
// linking to WhatsApp chat
CustomAnimatedContainer whatsapp() {
return CustomAnimatedContainer(
// Green theme for WhatsApp
containerColor: Colors.green,
borderColor: Colors.green,
imagePath: 'assets/whatsapp.png',
text: 'WhatsApp',
// Add your WhatsApp contact link here
linkUrl: '',
);
}
Now, we shall see the code for the CustomAnimatedContainer, which is the widget that is used as the instance for each social media function above. The code below defines a 'CustomAnimatedContainer' widget, which is a stateful widget. This widget is designed to create interactive animated containers representing social media links. It takes several parameters for customization: 'containerColor' (background color), 'borderColor', 'imagePath' (path to the image), 'text' (displayed text), and 'linkUrl' (URL to be opened when clicked).
- The '_CustomAnimatedContainerState' class manages the state of the widget. It has a '_isHovered' boolean variable to keep track of whether the container is being hovered over by the mouse.
- In the 'build' method, the widget renders a 'Container' that enforces minimum height and width constraints and adds a bottom margin. The 'InkWell' widget wraps the content of the container and handles the tap action to open the specified 'linkUrl' in a browser.
- Inside the 'InkWell', the 'MouseRegion' widget tracks mouse enter and exit events to update the '_isHovered' state. When the mouse enters the container, '_isHovered' is set to 'true', and when it exits, '_isHovered' is set to 'false'.
- The 'AnimatedContainer' within the 'MouseRegion' is where the visual representation of the social media link is created. It changes appearance smoothly when '_isHovered' changes. It has a dynamic width and height based on the screen size. It also has a background color defined by 'containerColor', a rounded corner, and a border that changes color based on '_isHovered'. A subtle shadow is added for depth.
The content of the container is composed of a 'Row' with three main elements:
- An 'Image.asset' widget displays the social media icon. The size of the icon is based on the screen size.
- A 'Text' widget displays the 'text' of the link, with white color, font weight, and font size based on the screen size.
- An 'AnimatedSwitcher' that shows different icons depending on '_isHovered'. It either displays a "touch" icon or an arrow icon.
The code creates a highly customizable, interactive, and visually appealing social media link container that smoothly transitions between states when hovered over or clicked, enhancing the user experience of the Flutter web app.
animated_container.dart:
Dart
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
class CustomAnimatedContainer extends StatefulWidget {
final Color containerColor;
final Color borderColor;
final String imagePath;
final String text;
final String linkUrl;
CustomAnimatedContainer({
required this.containerColor,
required this.borderColor,
required this.imagePath,
required this.text,
required this.linkUrl,
});
@override
_CustomAnimatedContainerState createState() =>
_CustomAnimatedContainerState();
}
class _CustomAnimatedContainerState extends State<CustomAnimatedContainer> {
bool _isHovered = false;
@override
Widget build(BuildContext context) {
return Container(
// constrains of min height and width
constraints: BoxConstraints(
minHeight: 50,
minWidth: 250,
),
margin:
EdgeInsets.only(bottom: MediaQuery.of(context).size.height * 0.02),
child: InkWell(
onTap: () {
// Open the link when the container is clicked
launch(widget.linkUrl);
},
child: MouseRegion(
onEnter: (_) {
setState(() {
_isHovered = true;
});
},
onExit: (_) {
setState(() {
_isHovered = false;
});
},
child: AnimatedContainer(
duration: Duration(milliseconds: 400),
height: MediaQuery.of(context).size.height * 0.063,
width: MediaQuery.of(context).size.width * 0.13,
decoration: BoxDecoration(
color: widget.containerColor,
borderRadius: BorderRadius.circular(15),
border: Border.all(
color: _isHovered ? widget.borderColor : Colors.white,
width: 2,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.5),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(0, 3),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
flex: 3,
child: Container(
padding: EdgeInsets.all(1),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: Image.asset(
widget.imagePath,
height: MediaQuery.of(context).size.height * 0.04,
width: MediaQuery.of(context).size.width * 0.04,
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.002,
),
Expanded(
flex: 8,
child: Text(
widget.text,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
fontSize: MediaQuery.of(context).size.height * 0.02,
),
),
),
AnimatedSwitcher(
duration: Duration(milliseconds: 400),
child: _isHovered
? Icon(
Icons.touch_app_outlined,
key: ValueKey<bool>(_isHovered),
color: Colors.white,
)
: Icon(
Icons.arrow_forward_ios,
key: ValueKey<bool>(_isHovered),
color: Colors.white,
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.005,
),
],
),
),
),
),
);
}
}
We also used a widget instance called Footer on our home page, so we shall see what its code looks like. The code defines a Footer widget for a Flutter web app. It features clickable icons and links. The _FooterState class manages its state, tracking hover, and containing a GitHub repository URL.
In the build method, an InkWell widget handles taps and hover events. Icons change color on hover. The footer displays "Made with" text, icons, and the developer's name.
The _launchUrl function launches URLs using url_launcher, handling launch success and errors. This interactive footer enhances user experience, providing GitHub attribution and easy URL access.
footer.dart:
Dart
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart' as url_launcher;
class Footer extends StatefulWidget {
@override
_FooterState createState() => _FooterState();
}
class _FooterState extends State<Footer> {
String url = '';
bool isHovered = false;
@override
Widget build(BuildContext context) {
return InkWell(
// Open the link when the container is clicked
onTap: () {
_launchUrl(Uri.parse(url));
},
onHover: (value) {
// Handle hover state
setState(() {
isHovered = value;
});
},
child: Container(
// color: Colors.black,
margin: EdgeInsets.only(top: 20, bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildIcon(Icons.devices, isHovered),
SizedBox(width: 5),
Text(
'Made with',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
SizedBox(width: 5),
_buildIcon(Icons.star, isHovered),
SizedBox(width: 5),
Text(
'Flutter by GFG',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
Widget _buildIcon(IconData icon, bool isHovered) {
return Icon(
icon,
// Change the color based on hover state
color: isHovered ? Colors.amber : Colors.white,
);
}
void _launchUrl(Uri url) async {
if (await url_launcher.canLaunch(url.toString())) {
await url_launcher.launch(url.toString());
} else {
throw 'Could not launch $url';
}
}
}
Now that we are done we can use the command below to check our app, and before doing so select the device as Chrome or any other browser. But you can also check it on a mobile device.
flutter run
To make a build compatible with the web i.e. flutter web use the command below:
flutter build web --release -v
You can see the file in the build folder which would contain a folder named web. This is the content that will be uploaded to Firebase.
Step 5: Firebase Deployment
Here I will give a quick overview of how to deploy the app we have created on Firebase hosting. But for more details you can check out this article here: Hosting Flutter Website On Firebase For Free
1. Create a new project on the Firebase console.
2. Register the app using the preferred method. In my case, I have simply used the method of adding a script in my index.html file.
3. Install Firebase CLI using the command command:
npm install -g firebase-tools
Just make sure you have the proper version of node installed on your system.
4. Initiate Firebase by using the below command but make sure you are logged in to Firebase with the correct credentials:
// login
firebase login
// starting services
firebase init
Choose hosting with a spacebar and move forward with the project that you just created. It will ask to create a public folder and we need to put all the content of the Flutter web build in this.
And use the command below the deploy it to the firebase. When successful it would give you the URL where you can asses that!
// replace Project ID with your project ID
firebase deploy --only Project ID
It's all done! Now is the time to flaunt your newly custom-created weblinks with Flutter! Enjoy!
For the Complete Application code refer to this Link : Click Here
Similar Reads
Sequence Diagrams - Unified Modeling Language (UML)
A Sequence Diagram is a key component of Unified Modeling Language (UML) used to visualize the interaction between objects in a sequential order. It focuses on how objects communicate with each other over time, making it an essential tool for modeling dynamic behavior in a system. Sequence diagrams
11 min read
90+ React Projects with Source Code [2025]
React, managed by Facebook and a vibrant community of developers and companies, is a JavaScript library designed to craft dynamic user interfaces. It empowers developers with concepts like React web apps, components, props, states, and component lifecycles. With a primary focus on building single-pa
12 min read
Flutter Tutorial
This Flutter Tutorial is specifically designed for beginners and experienced professionals. It covers both the basics and advanced concepts of the Flutter framework.Flutter is Googleâs mobile SDK that builds native Android and iOS apps from a single codebase. It was developed in December 2017. When
7 min read
Implementing Web Scraping in Python with BeautifulSoup
There are mainly two ways to extract data from a website:Use the API of the website (if it exists). For example, Facebook has the Facebook Graph API which allows retrieval of data posted on Facebook.Access the HTML of the webpage and extract useful information/data from it. This technique is called
8 min read
Dart Tutorial
Dart is an open-source general-purpose programming language developed by Google. It supports application development on both the client and server side. However, it is widely used for the development of Android apps, iOS apps, IoT(Internet of Things), and web applications using the Flutter Framework
7 min read
Learning Model Building in Scikit-learn
Building machine learning models from scratch can be complex and time-consuming. However with the right tools and frameworks this process can become significantly easier. Scikit-learn is one such tool that makes machine learning model creation easy. It provides user-friendly tools for tasks like Cla
9 min read
Design an Event Webpage using HTML and CSS
Creating an event webpage is an exciting way to showcase information about an event, including its schedule, speakers, and contact details. What Weâre Going to CreateWeâll create a webpage for a fictional event called "GeeksforGeeks TechCon 2025." This webpage will includeA header introducing the ev
5 min read
Python Django Projects with Source Code (Beginners to Advanced)
Python Django Projects with Source Code - Adding a project portfolio to your resume helps to show your skills and potential to your recruiter. Because in the tech space, real-time project experience matters a lot. Now, to make your resume powerful, we have come up with the top Django projects with s
5 min read
Top 50 Flutter Interview Questions and Answers for 2025
Flutter is an open-source, cross-platform application development framework. It was developed by Google in 2017. It is used to build applications for Android, iOS, Linux, Mac, Windows, and the web. Flutter uses the Dart programming language. It provides a simple, powerful, efficient, and easy-to-und
15+ min read
Credit Card Fraud Detection - ML
The goal of this project is to develop a machine learning model that can accurately detect fraudulent credit card transactions using historical data. By analyzing transaction patterns, the model should be able to distinguish between normal and fraudulent activity, helping financial institutions flag
6 min read