theme_extensions_gen 0.1.0 copy "theme_extensions_gen: ^0.1.0" to clipboard
theme_extensions_gen: ^0.1.0 copied to clipboard

Theme Extensions Generator

example/lib/main.dart

import 'dart:math';

import 'package:example/theme_manager.dart';
import 'package:example/theme_manager_scope.dart';
import 'package:flutter/material.dart';
import 'package:templates/generated/theme_extensions/context_extensions.dart';
import 'package:theme_dark/generated/theme_extensions.dart';
import 'package:theme_light/generated/theme_extensions.dart';

void main() {
  runApp(
    ThemeManagerScope(
      notifier: ThemeManager(themeExtensionsLight, themeExtensionsDark),
      child: const MyApp(),
    ),
  );
}

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

  @override
  Widget build(BuildContext context) {
    return BrandedAppBuilder(
      builder: (context, theme) {
        return MaterialApp(
          title: 'Theme Extensions Example',
          debugShowCheckedModeBanner: false,
          theme: theme,
          home: const MyHomePage(),
        );
      },
    );
  }
}

typedef BrandedAppThemeBuilder = Widget Function(
  BuildContext context,
  ThemeData theme,
);

class BrandedAppBuilder extends StatelessWidget {
  const BrandedAppBuilder({
    required this.builder,
    super.key,
  });

  final BrandedAppThemeBuilder builder;

  @override
  Widget build(BuildContext context) {
    final manager = ThemeManagerScope.of(context);

    return builder(
      context,
      _createThemeData(manager.isLight, manager.extensions),
    );
  }

  ThemeData _createThemeData(
    bool isLight,
    List<ThemeExtension> extensions,
  ) =>
      ThemeData(
        colorScheme: ColorScheme.fromSeed(
          brightness: isLight ? Brightness.light : Brightness.dark,
          seedColor: Colors.indigo,
        ),
        extensions: extensions,
        splashFactory: NoSplash.splashFactory,
      );
}

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

  @override
  Widget build(BuildContext context) {
    final textTheme = context.brandedTextTheme;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Theme Extensions Example'),
        centerTitle: true,
        elevation: 0,
        forceMaterialTransparency: true,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(24),
        child: Center(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            spacing: 20,
            children: [
              const Center(child: BrandedButton()),
              const BrandedCard(),
              Text('Title', style: textTheme.title),
              Text('Subtitle', style: textTheme.subtitle),
              Text('Primary text', style: textTheme.primaryText),
              Text('Secondary text', style: textTheme.secondaryText),
              const BrandedChips(),
            ],
          ),
        ),
      ),
      bottomNavigationBar: const BottomAppBar(
        child: Center(child: ThemeSwitcher()),
      ),
    );
  }
}

class ChipsStateNotifier extends ChangeNotifier {
  ChipsStateNotifier({
    required int count,
  }) : _selectedFlags = List.generate(count, (i) => Random().nextBool());

  final List<bool> _selectedFlags;

  bool isSelected(int index) => _selectedFlags[index];

  void toggle(int index) {
    _selectedFlags[index] = !_selectedFlags[index];
    notifyListeners();
  }
}

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

  static const _chipLabels = [
    'New',
    'Popular',
    'Trending',
    'Featured',
    'Recommended',
    'Sale',
    'Hot',
    'Exclusive',
    'Limited',
    'Best Seller',
  ];

  @override
  Widget build(BuildContext context) {
    return Wrap(
      spacing: 8,
      runSpacing: 8,
      children: [
        for (int i = 0; i < _chipLabels.length; i++)
          BrandedChip(
            isSelected: Random().nextBool(),
            text: _chipLabels[i],
          ),
      ],
    );
  }
}

class BrandedChip extends StatelessWidget {
  const BrandedChip({
    required this.isSelected,
    required this.text,
    super.key,
  });

  final bool isSelected;
  final String text;

  @override
  Widget build(BuildContext context) {
    final theme = context.brandedChipTheme;

    return Container(
      decoration: isSelected ? theme.decorationSelected : theme.decoration,
      padding: theme.padding,
      child: Text(
        text,
        style: isSelected ? theme.textStyleSelected : theme.textStyle,
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final manager = ThemeManagerScope.of(context);

    return Switch(
      value: manager.isLight,
      onChanged: (newState) => manager.toggle(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final theme = context.brandedButtonTheme;

    return Container(
      height: theme.height,
      decoration: theme.decoration,
      padding: theme.padding,
      child: Center(
        widthFactor: 1,
        child: Text('Branded Button', style: theme.textStyle, maxLines: 1),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final theme = context.brandedCardTheme;

    return Container(
      decoration: theme.decoration,
      padding: theme.padding,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            'Card Title',
            style: theme.titleStyle,
            maxLines: 1,
          ),
          SizedBox(height: theme.titleGapY),
          Text(
            _cardDescription,
            style: theme.descriptionStyle,
          ),
        ],
      ),
    );
  }
}

const _cardDescription = 'Discover new destinations with our travel guide. '
    'Explore hidden gems, top-rated attractions, and local favorites to make '
    'every trip unforgettable. Plan your journey with expert tips and '
    'recommendations tailored for you. From city escapes to nature retreats, '
    'find the perfect spot for any mood.';