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
Google Sign In With Firebase in Flutter Web
Firebase is a Google product that helps developers build, manage, and grow their apps easily. It also helps developers build their apps faster and more securely. No programming is required on the Firebase side, which makes it easy to use its features more efficiently. You can sign in with Google Sig
4 min read
Google Sign In With Firebase in Flutter
Firebase is a product of Google that helps developers to build, manage, and grow their apps easily. It helps developers build their apps faster and more securely. No programming is required on the Firebase side which makes it easy to use its features more efficiently. Google Sign-in with Firebase in
6 min read
Implementing Rest API in Flutter
Along with building a UI in Flutter, we can also integrate it with the backend. Most applications use APIs to display user data. We will use the http package, which provides advanced methods to perform operations. REST APIs use simple HTTP calls to communicate with JSON data because:It uses await
5 min read
Flutter - Read and Write Data on Firebase
Firebase helps developers to manage their mobile apps easily. It is a service provided by Google. Firebase has various functionalities available to help developers manage and grow their mobile apps. In this article, we will learn how to write and read data into/from Firebase. Follow the 3-step proce
4 min read
How to Add Firebase into Flutter in a New Way?
Recently Firebase give another option for adding Firebase to Flutter and It is only made for Flutter, As of now we add firebase using the android option for flutter but now we can add using the flutter option that is recently updated by Flutter. Here you can find how to add firebase as an Android in
2 min read
Flutter - Store User Details Using Firebase
Firebase is a product of Google that helps developers to build, manage, and grow their apps easily. It helps developers to build their apps faster and in a more secure way. No programming is required on the Firebase side which makes it easy to use its features more efficiently. It provides services
3 min read
Flutter - Implementing Signing Out the User
In this article, we can learn how to Signout the user from a flutter application. This article is a continuation of the article that explains making google signIn UI and its authentication. It is recommended to check the same before moving ahead. Now we will implement the Sign out feature for the fl
3 min read
Hosting Flutter Website On Firebase For Free
Flutter is a user interface development software development kit. Flutter is an open-source project maintained by Google. It enables software developers to make beautiful natively compiled applications for IOS, Android, Web, and Desktop with a single code base. Although developing flutter applicatio
4 min read
Remote Config in Flutter and Firebase
In Current app development, it is essential to offer smooth updates without asking users to download a new version from the Play Store to improve user experience. Remote Config in Flutter with the power of Firebase allows developers to update app content and features dynamically without releasing a
8 min read
Face Detection in Flutter using Firebase ML Kit
Face detection is a technique by which we can locate the human faces in the image given. Face detection is used in many places nowadays, especially websites hosting images like Picasa, Photobucket, and Facebook. The automatic tagging feature adds a new dimension to sharing pictures among the people
5 min read