Skip to content

Flutter ​

Build beautiful mobile applications with Galaxy UI components for Flutter.

Overview ​

Galaxy UI for Flutter provides 37 production-ready components styled with Material Design 3. All components are fully typed with Dart and follow Flutter best practices with StatelessWidget patterns.

Prerequisites ​

Before you begin, make sure you have:

  • Flutter 3.24+ (latest stable)
  • Dart 3.5+
  • Material Design 3 configured
  • An existing Flutter project

Installation ​

Step 1: Initialize Galaxy UI ​

bash
# Using npx
npx @galaxy-stack/nebula-cli@latest init

# Using bunx
bunx @galaxy-stack/nebula-cli@latest init

The CLI will:

  1. ✅ Detect Flutter automatically
  2. ✅ Add required dependencies to pubspec.yaml
  3. ✅ Create components.json configuration
  4. ✅ Set up Material Design 3 theme
  5. ✅ Create utility functions
  6. ✅ Export GalaxyButton widget with params: variant (6 options), size (4 options), onPressed, disabled, child

Step 2: Add Components ​

bash
# Add a single component
npx @galaxy-stack/nebula-cli@latest add button

# Add multiple components
npx @galaxy-stack/nebula-cli@latest add button input dialog

Configuration ​

Galaxy UI creates a components.json file in your project root:

json
{
  "$schema": "https://galaxy-nebula.vercel.app/schema.json",
  "framework": "flutter",
  "language": "dart",
  "styling": "material3",
  "material": {
    "version": "3",
    "theme": "lib/theme/theme.dart"
  },
  "aliases": {
    "components": "lib/components"
  }
}

Material Design 3 Setup ​

Make sure Material 3 is properly configured in your Flutter app:

pubspec.yaml ​

yaml
name: your_app
description: A beautiful Flutter application with Galaxy UI

dependencies:
  flutter:
    sdk: flutter

  # Material Design 3 is included in Flutter SDK
  # Additional dependencies added by Galaxy UI CLI:
  intl: ^0.19.0

flutter:
  uses-material-design: true

  # Optional: Add custom fonts
  fonts:
    - family: Inter
      fonts:
        - asset: fonts/Inter-Regular.ttf
        - asset: fonts/Inter-Medium.ttf
          weight: 500
        - asset: fonts/Inter-Bold.ttf
          weight: 700

lib/theme/theme.dart ​

dart
import 'package:flutter/material.dart';

class AppTheme {
  // Light Theme
  static ThemeData lightTheme = ThemeData(
    useMaterial3: true,
    colorScheme: ColorScheme.fromSeed(
      seedColor: const Color(0xFF6366F1), // Indigo
      brightness: Brightness.light,
    ),
    textTheme: const TextTheme(
      displayLarge: TextStyle(
        fontSize: 57,
        fontWeight: FontWeight.bold,
      ),
      titleLarge: TextStyle(
        fontSize: 22,
        fontWeight: FontWeight.w500,
      ),
      bodyLarge: TextStyle(
        fontSize: 16,
      ),
    ),
  );

  // Dark Theme
  static ThemeData darkTheme = ThemeData(
    useMaterial3: true,
    colorScheme: ColorScheme.fromSeed(
      seedColor: const Color(0xFF6366F1), // Indigo
      brightness: Brightness.dark,
    ),
    textTheme: const TextTheme(
      displayLarge: TextStyle(
        fontSize: 57,
        fontWeight: FontWeight.bold,
      ),
      titleLarge: TextStyle(
        fontSize: 22,
        fontWeight: FontWeight.w500,
      ),
      bodyLarge: TextStyle(
        fontSize: 16,
      ),
    ),
  );
}

lib/main.dart ​

dart
import 'package:flutter/material.dart';
import 'package:your_app/theme/theme.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Galaxy UI App',
      theme: AppTheme.lightTheme,
      darkTheme: AppTheme.darkTheme,
      themeMode: ThemeMode.system,
      home: const HomePage(),
    );
  }
}

Usage Examples ​

Button Component ​

dart
import 'package:flutter/material.dart';
import 'package:your_app/components/ui/button.dart';

class ButtonExample extends StatelessWidget {
  const ButtonExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        // Default button
        GalaxyButton(
          variant: ButtonVariant.defaultVariant,
          onPressed: () {
            print('Button pressed!');
          },
          child: const Text('Default Button'),
        ),

        const SizedBox(height: 16),

        // Secondary button
        GalaxyButton(
          variant: ButtonVariant.secondary,
          onPressed: () {},
          child: const Text('Secondary Button'),
        ),

        const SizedBox(height: 16),

        // Outline button
        GalaxyButton(
          variant: ButtonVariant.outline,
          onPressed: () {},
          child: const Text('Outline Button'),
        ),

        const SizedBox(height: 16),

        // Disabled button
        GalaxyButton(
          variant: ButtonVariant.defaultVariant,
          onPressed: null,
          child: const Text('Disabled Button'),
        ),
      ],
    );
  }
}

Input Component ​

dart
import 'package:flutter/material.dart';
import 'package:your_app/components/ui/input.dart';

class LoginForm extends StatefulWidget {
  const LoginForm({super.key});

  @override
  State<LoginForm> createState() => _LoginFormState();
}

class _LoginFormState extends State<LoginForm> {
  final _emailController = TextEditingController();
  final _passwordController = TextEditingController();

  @override
  void dispose() {
    _emailController.dispose();
    _passwordController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // Email input
          const Text(
            'Email',
            style: TextStyle(
              fontSize: 14,
              fontWeight: FontWeight.w500,
            ),
          ),
          const SizedBox(height: 8),
          GalaxyInput(
            controller: _emailController,
            hintText: 'Enter your email',
            keyboardType: TextInputType.emailAddress,
          ),

          const SizedBox(height: 16),

          // Password input
          const Text(
            'Password',
            style: TextStyle(
              fontSize: 14,
              fontWeight: FontWeight.w500,
            ),
          ),
          const SizedBox(height: 8),
          GalaxyInput(
            controller: _passwordController,
            hintText: 'Enter your password',
            obscureText: true,
          ),
        ],
      ),
    );
  }
}

Dialog Component ​

dart
import 'package:flutter/material.dart';
import 'package:your_app/components/ui/dialog.dart';
import 'package:your_app/components/ui/button.dart';

class DialogExample extends StatelessWidget {
  const DialogExample({super.key});

  void _showDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return GalaxyDialog(
          title: 'Confirm Action',
          content: const Text(
            'Are you sure you want to continue? This action cannot be undone.',
          ),
          actions: [
            GalaxyButton(
              variant: ButtonVariant.outline,
              onPressed: () {
                Navigator.of(context).pop();
              },
              child: const Text('Cancel'),
            ),
            GalaxyButton(
              variant: ButtonVariant.defaultVariant,
              onPressed: () {
                Navigator.of(context).pop();
                // Handle confirmation
              },
              child: const Text('Confirm'),
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return GalaxyButton(
      onPressed: () => _showDialog(context),
      child: const Text('Open Dialog'),
    );
  }
}

Available Components ​

Galaxy UI for Flutter includes 37 mobile-optimized components:

Form & Input ​

  • Button
  • Input
  • Checkbox
  • Radio Group
  • Select (Dropdown)
  • Slider
  • Switch
  • Textarea
  • Label

Layout ​

  • Separator (Divider)
  • Accordion (ExpansionPanel)
  • Collapsible
  • Tabs (TabBar)
  • Aspect Ratio
  • Sheet (BottomSheet)
  • Toolbar
  • Navigation Menu
  • Pagination

Data Display ​

  • Avatar
  • Progress (LinearProgressIndicator)
  • Badge
  • Typography
  • Empty State
  • Skeleton (Shimmer)

Overlays ​

  • Dialog
  • Alert Dialog
  • Popover (Menu)
  • Tooltip

Date & Time ​

  • Calendar (DatePicker)
  • Calendar Range (DateRangePicker)

Interactive ​

  • Toggle
  • Toggle Group (SegmentedButton)

Dark Mode ​

Galaxy UI supports dark mode automatically with Flutter's ThemeMode:

dart
import 'package:flutter/material.dart';

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  ThemeMode _themeMode = ThemeMode.system;

  void _toggleTheme() {
    setState(() {
      _themeMode = _themeMode == ThemeMode.light
          ? ThemeMode.dark
          : ThemeMode.light;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: AppTheme.lightTheme,
      darkTheme: AppTheme.darkTheme,
      themeMode: _themeMode,
      home: Scaffold(
        appBar: AppBar(
          actions: [
            IconButton(
              icon: Icon(
                _themeMode == ThemeMode.light
                    ? Icons.dark_mode
                    : Icons.light_mode,
              ),
              onPressed: _toggleTheme,
            ),
          ],
        ),
        body: const HomePage(),
      ),
    );
  }
}

State Management ​

Galaxy UI components work with any Flutter state management solution:

Provider ​

dart
import 'package:provider/provider.dart';

class CounterProvider extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

// Usage
GalaxyButton(
  onPressed: () {
    context.read<CounterProvider>().increment();
  },
  child: Text('Count: ${context.watch<CounterProvider>().count}'),
)

Riverpod ​

dart
import 'package:flutter_riverpod/flutter_riverpod.dart';

final counterProvider = StateProvider<int>((ref) => 0);

// Usage
Consumer(
  builder: (context, ref, child) {
    final count = ref.watch(counterProvider);
    return GalaxyButton(
      onPressed: () {
        ref.read(counterProvider.notifier).state++;
      },
      child: Text('Count: $count'),
    );
  },
)

Best Practices ​

1. Use const constructors ​

dart
// ✅ Good - uses const
const GalaxyButton(
  child: Text('Click Me'),
)

// ❌ Bad - no const
GalaxyButton(
  child: Text('Click Me'),
)

2. Extract widgets for reusability ​

dart
class CustomCard extends StatelessWidget {
  final String title;
  final String subtitle;

  const CustomCard({
    super.key,
    required this.title,
    required this.subtitle,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            Text(title, style: Theme.of(context).textTheme.titleLarge),
            Text(subtitle),
          ],
        ),
      ),
    );
  }
}

3. Use Theme.of(context) ​

dart
Text(
  'Hello World',
  style: Theme.of(context).textTheme.headlineSmall,
)

Performance Tips ​

  1. Use const constructors whenever possible
dart
const Padding(
  padding: EdgeInsets.all(8.0),
  child: Text('Hello'),
)
  1. Use ListView.builder for long lists
dart
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(title: Text(items[index]));
  },
)
  1. Cache complex calculations
dart
@override
Widget build(BuildContext context) {
  final filteredItems = useMemoized(
    () => items.where((item) => item.isActive).toList(),
    [items],
  );

  return ListView(children: filteredItems.map(...));
}

Troubleshooting ​

Material 3 colors not working ​

Make sure you have useMaterial3: true in your ThemeData:

dart
ThemeData(
  useMaterial3: true,
  colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
)

Components not importing correctly ​

Check your import paths match the configuration in components.json:

dart
import 'package:your_app/components/ui/button.dart';

Hot reload not working ​

Try:

  1. Hot restart (R in terminal)
  2. Stop and restart the app
  3. Run flutter clean then flutter pub get

Next Steps ​

Resources ​

Support ​

Having issues? Check our GitHub Issues or contact kevinbui210191@gmail.com

Released under the MIT License.