ui_kit_skeleton
UI Kit Core Brick is a powerful tool for generating a UI Kit Core package skeleton in your Flutter project. This skeleton provides a foundation for building a custom design system, including colors, text styles, theming, utility functions, and more. It enables you to establish a consistent and efficient workflow for developing UI components and styling in your Flutter applications.
Generated by [mason][1] š§±
Getting Started š
To get started with UI Kit Core using Mason CLI, follow the steps below:
1. Installation
Before proceeding, make sure you have Mason CLI installed on your machine. To check if it is installed, run the following command:
mason version
If you encounter an error while running this command, it means that Mason CLI is not yet activated on your machine. To activate it, use the following command:
dart pub global activate mason_cli
2. Adding ui_kit_core brick
To add this brick to your mason registry, use one of the following commands based on your preference:
# Install it locally
mason add ui_kit_core
# Install it globally
mason add -g ui_kit_core
Usage
You can watch an interactive tutorial on how to use this brick by visiting my YouTube video at the following link:
Eps. 2 of Flutter UI Kit Package Development - Using Mason Bricks
In the tutorial video, I provide step-by-step instructions and demonstrations on how to effectively utilize the UI Kit Core Brick in your Flutter projects. Feel free to watch the video to gain a better understanding of the brick's usage and maximize its potential in your UI Kit development workflow.
Before using this brick, you need to create a JSON file containing all the required input variables for generating your component. The following table provides the variables needed:
Name | Type | Description | Required |
---|---|---|---|
name | String | The name of the app to generate the UI Package for. It will be used as the prefix in the UI Kit. For example, facebook_ui_kit.dart . | Yes |
fontFamily | String | The font family to be used in your UI Kit. This must be a font family available on Google Fonts. | Yes |
prefix | String | The prefix to be used in core files. For example, fb_colors.dart , class FBColors , etc. | Yes |
githubUsername | String | The GitHub account username where you have hosted the code. It will be added to the pubspec.yaml homepage property. | Yes |
repositoryName | String | The name of the GitHub repository where your code is hosted. It will be added to the pubspec.yaml homepage property. | Yes |
colors | List | A list of colors from your figma design system. | Yes |
typography | List | A list of all text styles from your figma design system. | Yes |
textTheme | List | A list of overridden textTheme styles with your text style. | Yes |
IMPORTANT NOTES
- For the
colors
variable, it should be an array containing property items with the following structure:
/// If color is type of Color
{
"name": "primary", // ex. primarydark
"isMaterialColor": false, // Determined if the color a MaterialColor or Color
"value": "303F9F", // Hex representation of color. Ex. FFFFFF. Must be a
}
/// If color is type of MaterialColor
{
"name": "neutral", // ex. primarydark
"isMaterialColor": false, // Determined if the color a MaterialColor or Color
"defaultColor": "AEAEAE", // Default color of your MaterialColor
"value": [
{
"level": 10,
"value": "D9D9D9"
}
],
/// List of material color object withe level and value.
/// After generated, this color can be used like this netutral[10]
}
- For the
typograhpy
variable, it should be an array containing typography items with the following structure:
{
"name": "title", /// Name of the style
"fontSize": 42, /// font size of the style
"parameters": [ /// list of customizable parameter for this style.
{
"type": "Color?",
"name": "color"
},
{
"type": "FontWeight?",
"name": "fontWeight",
"default": "FontWeight.w800"
},
{
"type": "TextDecoration?",
"name": "decoration"
}
]
}
- For the
textTheme
variable, it should be an array containing enum items with the following structure:
/// Basic configuration
{
"name": "Display Large", /// Name of textTheme you want to override
"style": "title", /// Name of your text style to override given theme.
}
{
"name": "Display Medium", /// Name of textTheme you want to override
"style": "title", /// Name of your text style to override given theme.
"parameters": [ /// List of customizable parameter for this textTheme.
{
"type": "FontWeight",
"name": "fontWeight",
"value": "FontWeight.w600"
}
]
}
Feel free to customize the values according to your needs.
If you have created your JSON file, name it based on your preference, for example, core-config.json
. Then, you can run the following command to generate the UI Kit Core using Mason CLI:
mason make ui_kit_core -c core-config.json
Make sure you replace core-config.json
with the actual file name and path if it's located in a different directory. This command will utilize the JSON configuration file to generate the UI Kit Core based on the specified parameters.
Example
For example, if you have the following core-config.json
in your project:
{
"name": "Sikerja",
"prefix": "sk",
"fontFamily": "montserrat",
"githubUsername": "banua-coder",
"repositoryName": "sikerja-ui-kit",
"colors": [
{
"isMaterialColor": false,
"name": "primary",
"value": "303F9F"
},
{
"isMaterialColor": false,
"name": "primarySurface",
"value": "F5F5FA"
},
{
"isMaterialColor": false,
"name": "primaryFocus",
"value": "D6D9EC"
},
{
"isMaterialColor": false,
"name": "primaryBorder",
"value": "BABFDF"
},
{
"isMaterialColor": false,
"name": "primaryHover",
"value": "283584"
},
{
"isMaterialColor": false,
"name": "primaryPressed",
"value": "182050"
},
{
"name": "neutral",
"isMaterialColor": true,
"defaultColor": "AEAEAE",
"value": [
{
"level": 10,
"value": "D9D9D9"
},
{
"level": 20,
"value": "DCDCDC"
},
{
"level": 30,
"value": "D5D5D5"
},
{
"level": 40,
"value": "C9C9C9"
},
{
"level": 50,
"value": "AEAEAE"
},
{
"level": 60,
"value": "8E8E8E"
},
{
"level": 70,
"value": "696969"
},
{
"level": 80,
"value": "535353"
},
{
"level": 90,
"value": "393939"
},
{
"level": 100,
"value": "090909"
}
]
}
],
"typography": [
{
"name": "title",
"fontSize": 42,
"parameters": [
{
"type": "Color?",
"name": "color"
},
{
"type": "FontWeight?",
"name": "fontWeight",
"default": "FontWeight.w800"
},
{
"type": "TextDecoration?",
"name": "decoration"
}
]
},
{
"name": "heading1",
"fontSize": 34,
"parameters": [
{
"type": "Color?",
"name": "color"
},
{
"type": "FontWeight?",
"name": "fontWeight",
"default": "FontWeight.w700"
},
{
"type": "TextDecoration?",
"name": "decoration"
}
]
},
{
"name": "heading2",
"fontSize": 27,
"parameters": [
{
"type": "Color?",
"name": "color"
},
{
"type": "FontWeight?",
"name": "fontWeight",
"default": "FontWeight.w700"
},
{
"type": "TextDecoration?",
"name": "decoration"
}
]
},
{
"name": "heading3",
"fontSize": 21,
"parameters": [
{
"type": "Color?",
"name": "color"
},
{
"type": "FontWeight?",
"name": "fontWeight",
"default": "FontWeight.w700"
},
{
"type": "TextDecoration?",
"name": "decoration"
}
]
},
{
"name": "body1",
"fontSize": 17,
"parameters": [
{
"type": "Color?",
"name": "color"
},
{
"type": "FontWeight?",
"name": "fontWeight",
"default": "FontWeight.w400"
},
{
"type": "TextDecoration?",
"name": "decoration"
}
]
},
{
"name": "body2",
"fontSize": 14,
"parameters": [
{
"type": "Color?",
"name": "color"
},
{
"type": "FontWeight?",
"name": "fontWeight",
"default": "FontWeight.w400"
},
{
"type": "TextDecoration?",
"name": "decoration"
}
]
},
{
"name": "caption",
"fontSize": 11,
"parameters": [
{
"type": "Color?",
"name": "color"
},
{
"type": "FontWeight?",
"name": "fontWeight",
"default": "FontWeight.w400"
},
{
"type": "TextDecoration?",
"name": "decoration"
}
]
},
{
"name": "overline",
"fontSize": 8,
"parameters": [
{
"type": "Color?",
"name": "color"
},
{
"type": "FontWeight?",
"name": "fontWeight",
"default": "FontWeight.w400"
},
{
"type": "TextDecoration?",
"name": "decoration",
"default": "TextDecoration.underline"
}
]
}
],
"textTheme": [
{
"name": "Display Large",
"style": "title"
},
{
"name": "Display Medium",
"style": "title",
"parameters": [
{
"type": "FontWeight",
"name": "fontWeight",
"value": "FontWeight.w600"
}
]
},
{
"name": "Display Small",
"style": "title",
"parameters": [
{
"type": "FontWeight",
"name": "fontWeight",
"value": "FontWeight.w400"
}
]
},
{
"name": "Title Large",
"style": "heading1"
},
{
"name": "Title Medium",
"style": "heading2"
},
{
"name": "Title Small",
"style": "heading3"
},
{
"name": "Body Large",
"style": "body2"
},
{
"name": "Body Medium",
"style": "body1"
},
{
"name": "Body Small",
"style": "caption"
}
]
}
If you run the command mason make ui_kit_core -c core-config.json
, it will generate the following files based on the provided configuration:
pubspec.yaml
: Contains customized version of the file with all required package installed.
name: sikerja_ui_kit
description: A UI Kit package for Sikerja.
version: 0.0.1
homepage: https://github.com/banua-coder/sikerja-ui-kit.git
environment:
sdk: ">=3.0.1 <4.0.0"
flutter: ">=1.17.0"
dependencies:
extended_image: ^8.0.2
flutter:
sdk: flutter
flutter_screenutil: ^5.8.4
flutter_spinkit: ^5.2.0
flutter_svg: ^2.0.7
flutter_switch: ^0.3.2
google_fonts: ^5.1.0
lottie: ^2.4.0
nil: ^1.1.1
shimmer: ^3.0.0
tap_debouncer: ^2.2.0
dev_dependencies:
dart_code_metrics: ^4.19.2
flutter_lints: ^2.0.0
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
# assets:
# - assets/logo/
# - assets/images/
flutter_gen:
output: lib/src/core/assets/gen/
integrations:
flutter_svg: true
lottie: true
analysis-option.yaml
: Contains customized version of the file with some additional rules also includingdart_code_metrics
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
analyzer:
plugins:
- dart_code_metrics
exclude:
- "build/**"
dart_code_metrics:
metrics:
cyclomatic-complexity: 20
maximum-nesting-level: 5
number-of-parameters: 4
source-line-of-code: 50
....
lib/sikerja_ui_kit.dart
: Contains all the package export.
library sikerja_ui_kit;
// 3rd party packages
export 'package:flutter_screenutil/flutter_screenutil.dart';
export 'package:nil/nil.dart';
// components
// styles
export 'src/core/styles/sk_colors.dart';
export 'src/core/styles/sk_text_style.dart';
// themes
export 'src/core/theme/sk_color_theme.dart';
export 'src/core/theme/sk_theme.dart';
// utils
export 'src/core/utils/component_init.dart';
lib/src/core/styles/sk_colors.dart
: Contains all of the colors.
import 'package:flutter/material.dart';
class SKColors {
const SKColors._();
static const Color primary = Color(0xFF303F9F);
static const Color primarySurface = Color(0xFFF5F5FA);
static const Color primaryFocus = Color(0xFFD6D9EC);
static const Color primaryBorder = Color(0xFFBABFDF);
static const Color primaryHover = Color(0xFF283584);
static const Color primaryPressed = Color(0xFF182050);
static const MaterialColor neutral = MaterialColor(
0xFFAEAEAE,
{
10: Color(0xFFD9D9D9),
20: Color(0xFFDCDCDC),
30: Color(0xFFD5D5D5),
40: Color(0xFFC9C9C9),
50: Color(0xFFAEAEAE),
60: Color(0xFF8E8E8E),
70: Color(0xFF696969),
80: Color(0xFF535353),
90: Color(0xFF393939),
100: Color(0xFF090909),
},
);
}
lib/src/core/styles/sk_text_style.dart
: Contains all of the typography.
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import '../../../sikerja_ui_kit.dart';
class SKTextStyle {
const SKTextStyle._();
static TextStyle _base({
double fontSize = 14,
FontWeight? fontWeight = FontWeight.w400,
double letterSpacing = 0.0,
TextDecoration? decoration,
Color? color,
double? height,
}) =>
GoogleFonts.montserrat(
fontSize: fontSize * 1.sp,
fontWeight: fontWeight,
letterSpacing: letterSpacing * 1.sp,
height: height,
textBaseline: TextBaseline.alphabetic,
decoration: decoration,
locale: const Locale('en', 'US'),
color: color,
);
//TODO: Define your text style variant and your main text theme
static TextTheme mainTextTheme = TextTheme(
displayLarge: SKTextStyle.title(),
displayMedium: SKTextStyle.title(),
displaySmall: SKTextStyle.title(),
titleLarge: SKTextStyle.heading1(),
titleMedium: SKTextStyle.heading2(),
titleSmall: SKTextStyle.heading3(),
bodyLarge: SKTextStyle.body2(),
bodyMedium: SKTextStyle.body1(),
bodySmall: SKTextStyle.caption(),
);
static TextStyle title({
Color? color,
FontWeight? fontWeight = FontWeight.w800,
TextDecoration? decoration,
}) =>
_base(
fontSize: 42,
color: color,
fontWeight: fontWeight,
decoration: decoration,
);
static TextStyle heading1({
Color? color,
FontWeight? fontWeight = FontWeight.w700,
TextDecoration? decoration,
}) =>
_base(
fontSize: 34,
color: color,
fontWeight: fontWeight,
decoration: decoration,
);
static TextStyle heading2({
Color? color,
FontWeight? fontWeight = FontWeight.w700,
TextDecoration? decoration,
}) =>
_base(
fontSize: 27,
color: color,
fontWeight: fontWeight,
decoration: decoration,
);
static TextStyle heading3({
Color? color,
FontWeight? fontWeight = FontWeight.w700,
TextDecoration? decoration,
}) =>
_base(
fontSize: 21,
color: color,
fontWeight: fontWeight,
decoration: decoration,
);
static TextStyle body1({
Color? color,
FontWeight? fontWeight = FontWeight.w400,
TextDecoration? decoration,
}) =>
_base(
fontSize: 17,
color: color,
fontWeight: fontWeight,
decoration: decoration,
);
static TextStyle body2({
Color? color,
FontWeight? fontWeight = FontWeight.w400,
TextDecoration? decoration,
}) =>
_base(
fontSize: 14,
color: color,
fontWeight: fontWeight,
decoration: decoration,
);
static TextStyle caption({
Color? color,
FontWeight? fontWeight = FontWeight.w400,
TextDecoration? decoration,
}) =>
_base(
fontSize: 11,
color: color,
fontWeight: fontWeight,
decoration: decoration,
);
static TextStyle overline({
Color? color,
FontWeight? fontWeight = FontWeight.w400,
TextDecoration? decoration = TextDecoration.underline,
}) =>
_base(
fontSize: 8,
color: color,
fontWeight: fontWeight,
decoration: decoration,
);
}
lib/src/core/theme/sk_color_theme.dart
: Contains all colors in ThemeExtension version.
import 'package:flutter/material.dart';
import '../../../sikerja_ui_kit.dart';
class SKColorTheme extends ThemeExtension<SKColorTheme> {
const SKColorTheme({
this.primary,
this.primarySurface,
this.primaryFocus,
this.primaryBorder,
this.primaryHover,
this.primaryPressed,
this.neutral10,
this.neutral20,
this.neutral30,
this.neutral40,
this.neutral50,
this.neutral60,
this.neutral70,
this.neutral80,
this.neutral90,
this.neutral100,
});
final Color? primary;
final Color? primarySurface;
final Color? primaryFocus;
final Color? primaryBorder;
final Color? primaryHover;
final Color? primaryPressed;
final Color? neutral10;
final Color? neutral20;
final Color? neutral30;
final Color? neutral40;
final Color? neutral50;
final Color? neutral60;
final Color? neutral70;
final Color? neutral80;
final Color? neutral90;
final Color? neutral100;
@override
ThemeExtension<SKColorTheme> copyWith({
Color? primary,
Color? primarySurface,
Color? primaryFocus,
Color? primaryBorder,
Color? primaryHover,
Color? primaryPressed,
Color? neutral10,
Color? neutral20,
Color? neutral30,
Color? neutral40,
Color? neutral50,
Color? neutral60,
Color? neutral70,
Color? neutral80,
Color? neutral90,
Color? neutral100,
}) =>
SKColorTheme(
primary: primary ?? this.primary,
primarySurface: primarySurface ?? this.primarySurface,
primaryFocus: primaryFocus ?? this.primaryFocus,
primaryBorder: primaryBorder ?? this.primaryBorder,
primaryHover: primaryHover ?? this.primaryHover,
primaryPressed: primaryPressed ?? this.primaryPressed,
neutral10: neutral10 ?? this.neutral10,
neutral20: neutral20 ?? this.neutral20,
neutral30: neutral30 ?? this.neutral30,
neutral40: neutral40 ?? this.neutral40,
neutral50: neutral50 ?? this.neutral50,
neutral60: neutral60 ?? this.neutral60,
neutral70: neutral70 ?? this.neutral70,
neutral80: neutral80 ?? this.neutral80,
neutral90: neutral90 ?? this.neutral90,
neutral100: neutral100 ?? this.neutral100,
);
@override
ThemeExtension<SKColorTheme> lerp(
covariant ThemeExtension<SKColorTheme>? other,
double t,
) {
if (other is! SKColorTheme) {
return this;
}
return SKColorTheme(
primary: Color.lerp(primary, other.primary, t),
primarySurface: Color.lerp(primarySurface, other.primarySurface, t),
primaryFocus: Color.lerp(primaryFocus, other.primaryFocus, t),
primaryBorder: Color.lerp(primaryBorder, other.primaryBorder, t),
primaryHover: Color.lerp(primaryHover, other.primaryHover, t),
primaryPressed: Color.lerp(primaryPressed, other.primaryPressed, t),
neutral10: Color.lerp(neutral10, other.neutral10, t),
neutral20: Color.lerp(neutral20, other.neutral20, t),
neutral30: Color.lerp(neutral30, other.neutral30, t),
neutral40: Color.lerp(neutral40, other.neutral40, t),
neutral50: Color.lerp(neutral50, other.neutral50, t),
neutral60: Color.lerp(neutral60, other.neutral60, t),
neutral70: Color.lerp(neutral70, other.neutral70, t),
neutral80: Color.lerp(neutral80, other.neutral80, t),
neutral90: Color.lerp(neutral90, other.neutral90, t),
neutral100: Color.lerp(neutral100, other.neutral100, t),
);
}
/// Light color scheme
static final SKColorTheme light = SKColorTheme(
primary: SKColors.primary,
primarySurface: SKColors.primarySurface,
primaryFocus: SKColors.primaryFocus,
primaryBorder: SKColors.primaryBorder,
primaryHover: SKColors.primaryHover,
primaryPressed: SKColors.primaryPressed,
neutral10: SKColors.neutral[10],
neutral20: SKColors.neutral[20],
neutral30: SKColors.neutral[30],
neutral40: SKColors.neutral[40],
neutral50: SKColors.neutral[50],
neutral60: SKColors.neutral[60],
neutral70: SKColors.neutral[70],
neutral80: SKColors.neutral[80],
neutral90: SKColors.neutral[90],
neutral100: SKColors.neutral[100],
);
/// Dark color scheme
static final SKColorTheme dark = SKColorTheme(
primary: SKColors.primary,
primarySurface: SKColors.primarySurface,
primaryFocus: SKColors.primaryFocus,
primaryBorder: SKColors.primaryBorder,
primaryHover: SKColors.primaryHover,
primaryPressed: SKColors.primaryPressed,
neutral10: SKColors.neutral[10],
neutral20: SKColors.neutral[20],
neutral30: SKColors.neutral[30],
neutral40: SKColors.neutral[40],
neutral50: SKColors.neutral[50],
neutral60: SKColors.neutral[60],
neutral70: SKColors.neutral[70],
neutral80: SKColors.neutral[80],
neutral90: SKColors.neutral[90],
neutral100: SKColors.neutral[100],
);
@override
String toString() {
return 'SKColorTheme('
'primary: $primary, '
'primarySurface: $primarySurface, '
'primaryFocus: $primaryFocus, '
'primaryBorder: $primaryBorder, '
'primaryHover: $primaryHover, '
'primaryPressed: $primaryPressed, '
'neutral10: $neutral10, '
'neutral20: $neutral20, '
'neutral30: $neutral30, '
'neutral40: $neutral40, '
'neutral50: $neutral50, '
'neutral60: $neutral60, '
'neutral70: $neutral70, '
'neutral80: $neutral80, '
'neutral90: $neutral90, '
'neutral100: $neutral100, '
')';
}
}
lib/src/core/theme/sk_theme.dart
: Contains all theme configuration.
import 'package:flutter/material.dart';
import '../../../sikerja_ui_kit.dart';
class SKTheme {
static ThemeData main() => ThemeData(
primaryColor: SKColors.primary,
textTheme: SKTextStyle.mainTextTheme,
);
}
lib/src/core/utils/component_init.dart
: Contains component initialization forScreenUtil
.
import 'package:flutter/material.dart';
import '../../../sikerja_ui_kit.dart';
class SKComponentInit extends StatelessWidget {
const SKComponentInit({super.key, required this.builder});
final Widget Function(BuildContext context) builder;
@override
Widget build(BuildContext context) => MediaQuery(
data: MediaQueryData.fromView(View.of(context)),
child: LayoutBuilder(
builder: (_, constraints) {
if(constraints.maxWidth != 0) {
ScreenUtil.init(
_,
designSize: Size(
constraints.maxWidth,
constraints.maxHeight,
),
);
return builder(context);
}
return nil;
},
),
);
}
test/util/sk_component_init_test.dart
: Contains component initialization for widget test.test/util/widget_test_scenario_model.dart
: Model for widget test scenario.example/pubspec.yaml
: Contains all pubspec configuration for the example app.example/analysis-options.yaml
: Contains all linter rule for the example app.example/lib/pages/home_page.dart
: Home page of example app.example/lib/pages/color_page.dart
: Page to preview all UI Kit Colors.example/lib/pages/typography_page.dart
: Page to preview all UI Kit typography.example/lib/app.dart
: Main app of the example page. Contains an example of how to initialize the component.example/lib/main.dart
: Main entry of the example app.
Support
Please support me in continuously developing useful bricks and packages like this by liking and subscribing. Feel free to provide your feedback and suggestions through the comments.
Help Banua Coder continue to provide valuable content:
Connect with Banua Coder:
- Instagram: Banua Coder
- Website: Banua Coder
- GitHub: Banua Coder
For my personal social media accounts:
- Instagram: ryanaidilp
- GitHub: ryanaidilp
- LinkedIn: ryanaidilp
- Twitter: ryanaidilp