Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
<<<<<<< HEAD
# Level 3.1: Introduction to Flutter

# Table of Content
=======
# ![Alt text](/readme/banner.png)

## Table of Content
>>>>>>> 2c618630675f3fbb648516611ff950a54e941594

- [Overview](#overview)
- [Learning Objectives](#learning-objectives)
Expand Down
Binary file added assets/logos/github_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/logos/x_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/profile_pic.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions lib/default_colors.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:flutter/material.dart';

class DefaultColors {
static const Color profileCardColor = Color.fromARGB(255, 159, 141, 238);

static const Color fieldColor = Color.fromARGB(255, 47, 45, 56);
static const Color fieldOutlineColor = Color.fromARGB(255, 75, 74, 83);

static const Color blockColor = Color.fromARGB(255, 39, 35, 43);

// This is done for convenience's sake, I find that matching the blocks outline with the field color gives a nice look
// so this is just a way for me to avoid having to reset both the block outline and the field color each time I change it
static const Color blockOutlineColor = fieldColor;
}
11 changes: 5 additions & 6 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'screens/profile_page.dart';

void main() {
runApp(const MainApp());
Expand All @@ -9,12 +10,10 @@ class MainApp extends StatelessWidget {

@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: Text('Hello World!'),
),
),
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(),
home: const ProfilePage(),
);
}
}
121 changes: 121 additions & 0 deletions lib/screens/profile_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import 'package:flutter/material.dart';
import '../widgets/description_field.dart';
import '../widgets/profile_card.dart';
import '../widgets/description_block.dart';

class ProfilePage extends StatelessWidget {
const ProfilePage({
super.key,
this.blockPadding = 20,
});

final double blockPadding;

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ListView(
physics: const BouncingScrollPhysics(),
addAutomaticKeepAlives: true,
// All the items in the list are wrapped with a Padding widget to create space between the items, that is the "blockPadding"
children: [
// Wrapped padding with UnconstrainedBox widget because of flutter weirdness wrt ListViews
const Padding(
padding: EdgeInsets.only(top: 20, bottom: 40),
child: ProfileCard(
name: "Fahad Al-Qarni",
profilePicture: AssetImage("assets/profile_pic.jpg"),
),
),

Padding(
padding: EdgeInsets.only(bottom: blockPadding),
child: const DescriptionBlock(
label: "Personal Details",
descriptions: [
DescriptionField(
label: "Email",
content: "fahad.a.s.algarni@gmail.com",
icon: Icons.email,
),
DescriptionField(
label: "Location",
content: "Dhahran, Eastern Province",
icon: Icons.location_on,
),
DescriptionField(
label: "University",
content: "Imam Abdulrahman bin Faisal University",
icon: Icons.school,
),
DescriptionField(
label: "Birthday",
content: "July 31st",
icon: Icons.celebration,
),
],
),
),

Padding(
padding: EdgeInsets.only(bottom: blockPadding),
child: const DescriptionBlock(
label: "Social Media",
descriptions: [
DescriptionField(
label: "X (Twitter)",
content: "@AlqarniDev",
image: Image(
image: AssetImage('assets/logos/x_logo.png'),
height: 40,
),
),
DescriptionField(
label: "Github",
content: "fahadasq",
image: Image(
image: AssetImage("assets/logos/github_logo.png"),
color: Colors.white,
height: 40,
),
),
],
),
),

Padding(
padding: EdgeInsets.only(bottom: blockPadding),
child: const DescriptionBlock(
label: "About Me",
descriptions: [
DescriptionField(
label: "Hobby",
content: "Gaming",
icon: Icons.videogame_asset,
),
DescriptionField(
label: "What do I do?",
content:
"Currently, I am a student at IAU studying business administration.",
),
DescriptionField(
label: "What type of games do I like?",
content:
"Alot! I like many games, there are only a few types of games I don't like. "
"Such as long arduous RPGs, or certain types of multiplayer games.\nMost of all, I love singleplayer games, especially (but not exclusively!) action games.",
),
DescriptionField(
label: "What do I love to do?",
content:
"Program! I love programming, and have been a self-taught programmer for a long time.\n"
"I especially love graphics programming and games programming.")
],
),
)
],
),
),
);
}
}
43 changes: 43 additions & 0 deletions lib/utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';

BoxDecoration roundedRectangle(Color color,
{Color outlineColor = Colors.black,
double roundness = 25,
double outlineThickness = 0.5,
bool shadow = false,
Color shadowColor = const Color.fromARGB(60, 0, 0, 0),
double shadowIntensity = 1,
bool bloom = false,
double bloomIntensity = 10}) {
// Initialize BoxShadows list with empty array
// Later add bloom and shadow BoxShadows to the list, if they're enabled
final List<BoxShadow> shadows = [];

// Essentially, bloom is just a BoxShadow with the same color of the rectangle and a high blur radius
if (bloom) {
BoxShadow bloom = BoxShadow(
color: color,
blurRadius: bloomIntensity,
offset: const Offset(0, 2),
);

shadows.add(bloom);
}

if (shadow) {
BoxShadow shadow = BoxShadow(
color: shadowColor,
blurRadius: shadowIntensity,
offset: const Offset(2, 2),
);

shadows.add(shadow);
}

return BoxDecoration(
color: color,
border: Border.all(width: outlineThickness, color: outlineColor),
borderRadius: BorderRadius.all(Radius.circular(roundness)),
boxShadow: shadows,
);
}
74 changes: 74 additions & 0 deletions lib/widgets/description_block.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import 'package:flutter/material.dart';
import 'description_field.dart';
import '../default_colors.dart';
import '../utils.dart';

class DescriptionBlock extends StatelessWidget {
const DescriptionBlock({
super.key,
required this.label,
required this.descriptions,
this.blockColor = DefaultColors.blockColor,
this.outlineColor = DefaultColors.blockOutlineColor,
this.labelFieldColor = DefaultColors.fieldColor,
this.labelFieldOutlineColor = DefaultColors.fieldOutlineColor,
this.fieldPadding = 24,
});

final String label;
final List<DescriptionField> descriptions;
final Color blockColor;
final Color outlineColor;
final Color labelFieldColor;
final Color labelFieldOutlineColor;
final double fieldPadding;

// Literally returns a "block" of description fields, with a label ontop and the column of description fields below it
@override
Widget build(BuildContext context) {
// Initialize the list of widgets to be displayed with a container widget for the block label
final List<Widget> displayWidgets = [
Container(
// Vertical margin to leave space between the blocks top border, and the list items below
margin: const EdgeInsetsDirectional.symmetric(vertical: 10),
// Some internal padding
padding: const EdgeInsets.all(12),
// A sorta text bubble for the label
decoration: roundedRectangle(
labelFieldColor,
outlineColor: labelFieldOutlineColor,
bloomIntensity: 32,
),
// And the text itself
child: Text(label),
),
];

// Essentially wrap each description widget with padding on the bottom side to leave space in the column for the next item
for (int i = 0; i < descriptions.length; i++) {
// Create the padding widget, with the description field as its child
Widget widget = Padding(
padding: EdgeInsets.only(bottom: fieldPadding),
child: descriptions[i],
);
// Then add it to display list
displayWidgets.add(widget);
}

return Container(
// Horizontal Margin
margin: const EdgeInsetsDirectional.symmetric(horizontal: 10),
// The actual "Block" itself
decoration: roundedRectangle(
blockColor,
outlineColor: outlineColor,
outlineThickness: 1.25,
),

child: Column(
mainAxisSize: MainAxisSize.min,
children: displayWidgets,
),
);
}
}
105 changes: 105 additions & 0 deletions lib/widgets/description_field.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import 'package:flutter/material.dart';
import '../default_colors.dart';
import '../utils.dart';

class DescriptionField extends StatelessWidget {
const DescriptionField(
{super.key,
required this.label,
required this.content,
this.fieldColor = DefaultColors.fieldColor,
this.outlineColor = DefaultColors.fieldOutlineColor,
this.icon,
this.iconColor,
this.image});

final String label;
final String content;
final Color fieldColor;
final Color outlineColor;
// If icon is non-null, image will be discarded
final IconData? icon;
final Color? iconColor;
final Image? image;

@override
Widget build(BuildContext context) {
// Determine whether to display icon, image, or leave visual blank
final Widget? visualWidget;

if (icon != null) {
visualWidget = Icon(
icon!,
size: 40,
color: iconColor,
);
} else if (image != null) {
visualWidget = image;
} else {
visualWidget = null;
}

// A column containing the label ontop, and the description field below it
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// ================= The Label =================
Padding(
// Padding below to leave space between label and description field
padding: const EdgeInsets.only(bottom: 8.0),
child: Container(
// Text left to right + horizontal margin
alignment: Alignment.centerLeft,
margin: const EdgeInsetsDirectional.symmetric(horizontal: 20),
// The Label Text
child: Text(
label,
style: const TextStyle(fontSize: 14),
),
),
),

// ======== The Description field ========
// A container that has a Stack widget as it's child
// The stack contains the text description, and the visual widget
Container(
// Horizontal margin + internal padding
margin: const EdgeInsetsDirectional.symmetric(horizontal: 10),
padding: const EdgeInsets.all(10),
// The actual field decoration
decoration: roundedRectangle(
fieldColor,
outlineColor: outlineColor,
),

// Description stack, for text and visual
child: Stack(
// Default alignment on center-left
alignment: Alignment.centerLeft,
children: [
// ========= Description Text =========
Container(
margin: EdgeInsets.only(
// If visual widget is non-null, expand margin on right side to leave space for the widget
right: (visualWidget != null) ? 60 : 5,
left: 5,
),
child: Text(
content,
style: const TextStyle(fontSize: 14),
),
),
// ========= Description Visual =========
Container(
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 6),
// visualWidget is null if there's no visual set
child: visualWidget,
),
],
),
)
],
);
}
}
Loading