Flutter | Starting New Project Basic Setup Guide

Published on October 30, 2025 6 min read

When starting a new Flutter project, it’s tempting to jump straight into building screens and features.
However, a proper initial setup can save you countless hours down the line — especially when your app starts to grow.

In this article, we’ll go through a step-by-step guide on how to set up a scalable and maintainable Flutter project, focusing on these key areas:

  1. Flavor setup (without external packages)
  2. Localization setup (using Flutter’s built-in tools)
  3. Asset generation (with flutter_gen)
  4. Theme setup (scalable, UI/UX-friendly theming)
  5. Network setup (using Dio)
  6. Navigation setup (wrapper-based, extendable for deep links)
  7. Logger setup (debug console and in-app request viewer)

1. Flavor Setup (No External Packages)

Flavors help you maintain different environments — like development, staging, and production— in a clean and maintainable way.

Instead of using a third-party package, you can use Flutter’s built-in flavor support through flutter run and Gradle/Xcode configurations.

Android side: build.gradle

flavorDimensions "default"
productFlavors {
dev {
applicationIdSuffix ".dev"
versionNameSuffix "-dev"
resValue "string", "app_name", "MyApp Dev"
}
prod {
resValue "string", "app_name", "MyApp"
}
}

If your Flutter project uses Gradle Kotlin DSL (build.gradle.kts), the flavor setup syntax changes slightly compared to Groovy.

android {
namespace = "com.example.myapp"
compileSdk = 34

defaultConfig {
applicationId = "com.example.myapp"
minSdk = 23
targetSdk = 34
versionCode = 1
versionName = "1.0"
}

flavorDimensions += "default"

productFlavors {
create("dev") {
dimension = "default"
applicationIdSuffix = ".dev"
versionNameSuffix = "-dev"
resValue("string", "app_name", "MyApp Dev")
}

create("prod") {
dimension = "default"
resValue("string", "app_name", "MyApp")
}
}

buildTypes {
getByName("debug") {
isDebuggable = true
}
getByName("release") {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android.txt"),
"proguard-rules.pro"
)
}
}
}

If you use different Firebase configs or app icons per flavor, you can store them under:

android/app/src/dev/
android/app/src/prod/

and Gradle will automatically pick the correct resources for each flavor.

Unlike Android, iOS doesn’t have a direct “flavor” concept — it uses Schemes and Build Configurations.
We’ll mirror the same environments (dev, prod) that we used on Android.

iOS Side: XCode

Step 1:

  • Go to: Runner → Project → Info tab
  • Under “Configurations”, duplicate the existing ones
  • Debug → rename copy to Debug-dev
  • Release → rename copy to Release-prod

Step 2:

  1. In Xcode’s top bar, click Runner → Manage Schemes
  2. Duplicate the existing Runner scheme twice:
  • Rename one to Runner-dev
  • Rename the other to Runner-prod
  1. Edit each scheme:
  • For Runner-dev, select the “Build Configuration” as Debug-dev.
  • For Runner-prod, select the “Build Configuration” as Release-prod.

Step 3:

In ios/Runner/Info.plist, you’ll likely have something like:

<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>

Now, open your Build Settings → search for “Product Bundle Identifier”,
and define separate IDs per configuration, e.g.:

dev → com.example.myapp.dev
prod → com.example.myapp

To make Flutter recognize them, ensure you also defined your flavors in ios/Runner’s xcconfig files.

Example: ios/Flutter/Debug-dev.xcconfig

#include "Generated.xcconfig"
FLUTTER_TARGET=lib/main.dart

Example: ios/Flutter/Release-prod.xcconfig

#include "Generated.xcconfig"
FLUTTER_TARGET=lib/main.dart

In VS Code, open or create .vscode/launch.json file and add entries like this:

{
"version": "0.1.0",
"configurations": [
{
"name": "Run Dev Flavor",
"request": "launch",
"type": "dart",
"program": "lib/main.dart",
"args": [
"--flavor",
"dev",
"--dart-define",
"FLAVOR=dev"
]
},
{
"name": "Run Prod Flavor",
"request": "launch",
"type": "dart",
"program": "lib/main.dart",
"args": [
"--flavor",
"prod",
"--dart-define",
"FLAVOR=prod"
]
}
]
}

2. Localization Setup (Built-in Flutter Way)

Flutter supports localization out-of-the-box with the flutter_localizations package — no need for third-party solutions.

Step 1: Enable localization in pubspec.yaml

flutter:
generate: true
uses-material-design: true
generate-localized-resources: true

Step 2: Add supported locales

return MaterialApp(
supportedLocales: [
Locale('en'),
Locale('tr'),
],
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
);

Step 3: Add your translation files, create lib/l10n folder and add:

app_en.arb
app_tr.arb

Then run:

flutter gen-l10n

This automatically generates AppLocalizations— a strongly typed localization class.

3. Asset Generation (Using flutter_gen)

To avoid hardcoded asset paths and typo errors, you can use flutter_gen, which automatically generates Dart accessors for your assets.

Step 1: Add to your pubspec.yaml

dev_dependencies:
flutter_gen_runner:

flutter:
assets:
- assets/images/
- assets/icons/

Step 2: Generate

flutter pub run build_runner build

Then use:

Image.asset(Assets.images.logo.path);

4. Theme Setup (Custom Colors and Standardization)

Your theme is the foundation of UI consistency. Setting it up properly early on helps your design system scale smoothly.

Step 1: Define color schemes, create a theme folder with app_colors.dart:

class AppColors {
static const primary = Color(0xFF0057D9);
static const secondary = Color(0xFF00BFA5);
static const background = Color(0xFFF5F5F5);
}

Step 2: Define ThemeData

final lightTheme = ThemeData(
colorScheme: ColorScheme.light(
primary: AppColors.primary,
secondary: AppColors.secondary,
),
scaffoldBackgroundColor: AppColors.background,
appBarTheme: const AppBarTheme(elevation: 0),
);

Step 3: Use UI/UX guidelines

  • Define spacing constants (AppSpacing.small, AppSpacing.medium, etc.)
  • Use text themes with semantic naming (headline, body, caption).
  • Don’t hardcode colors or font sizes in widgets.

5. Network Setup (Using Dio)

For a robust and modern network layer, Dio is still one of the best options in 2025.

Step 1: Create a network folder with api_client.dart

import 'package:dio/dio.dart';

class ApiClient {
final Dio _dio;

ApiClient({String? baseUrl})
: _dio = Dio(BaseOptions(baseUrl: baseUrl ?? "https://api.example.com"));

Future<Response<T>> get<T>(String path) async {
return _dio.get<T>(path);
}

Future<Response<T>> post<T>(String path, dynamic data) async {
return _dio.post<T>(path, data: data);
}
}

Step 2: Extend for error handling, interceptors, and token refresh later. Keep ApiClient stateless and inject dependencies via constructors — makes testing easier.

6. Navigation Setup (Universal Wrapper)

Whether you’re using go_router, auto_route, or built-in Navigator, a wrapper helps abstract navigation logic.

import 'package:flutter/material.dart';

class NavigationManager {
static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

static Future<T?> push<T>(Route<T> route) =>
navigatorKey.currentState?.push(route);

static void pop<T>([T? result]) =>
navigatorKey.currentState?.pop(result);

static Future<T?> pushNamed<T>(String route, {Object? arguments}) =>
navigatorKey.currentState?.pushNamed(route, arguments: arguments);
}

Then, in your Material app:

navigatorKey: NavigationManager.navigatorKey,

This setup lets you:

  • Navigate globally without BuildContext.
  • Manage deep links and universal links from a single source later.

Create a DeepLinkHandler class that parses incoming URIs and maps them to routes using the same manager.

7. Logger Setup (Console + Network Tracking)

Logging is essential for debugging complex flows — especially network requests.

Step 1: Console logger

final log = Logger();

log.i("App started");
log.e("Error occurred", error, stackTrace);

Step 2: In-app network inspector

For monitoring requests:

Both integrate with Dio:

final alice = Alice();
final dio = Dio()..interceptors.add(alice.getDioInterceptor());

Use verbose logging only in dev flavor. Avoid exposing sensitive data in logs for production.

Thanks for reading. Happy weekends 🎈.

Flutter | Starting New Project Basic Setup Guide

Category: software-engineeringTags: software-engineering, flutter, computer-science, mobile-app-development, flutter-app-development