← Назад к вопросам

Как организуешь код стайл в приложении?

1.2 Junior🔥 191 комментариев
#Архитектура Flutter#ООП и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Ответ

Организация кода стайла в Flutter приложении — это критическая часть создания удобного и единообразного интерфейса. Существует несколько стратегий, каждая с плюсами и минусами.

Основная проблема: дублирование стилей

// Плохо — стили дублируются везде
Container(
  padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
  decoration: BoxDecoration(
    color: Colors.white,
    borderRadius: BorderRadius.circular(8),
    boxShadow: [BoxShadow(blurRadius: 8)],
  ),
  child: Text(
    'Button',
    style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
  ),
)

Способ 1: Centralised Theme (встроенный ThemeData)

Самый стандартный подход, используется в Material Design:

MaterialApp(
  theme: ThemeData(
    useMaterial3: true,
    colorScheme: ColorScheme.fromSeed(
      seedColor: Colors.blue,
      brightness: Brightness.light,
    ),
    textTheme: TextTheme(
      headlineLarge: TextStyle(
        fontSize: 24,
        fontWeight: FontWeight.bold,
        color: Colors.black,
      ),
      bodyMedium: TextStyle(
        fontSize: 14,
        color: Colors.grey[700],
      ),
    ),
    inputDecorationTheme: InputDecorationTheme(
      border: OutlineInputBorder(
        borderRadius: BorderRadius.circular(8),
      ),
      contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
    ),
  ),
)

// Использование в коде
Text('Hello', style: Theme.of(context).textTheme.headlineLarge)
TextField(
  decoration: InputDecoration(hintText: 'Enter text'),
)

Способ 2: Custom Constants класс

Для более гибкого управления:

class AppStyles {
  // Colors
  static const primaryColor = Color(0xFF2196F3);
  static const secondaryColor = Color(0xFF1E88E5);
  static const backgroundColor = Color(0xFFF5F5F5);
  static const errorColor = Color(0xFFD32F2F);

  // Text Styles
  static const headingStyle = TextStyle(
    fontSize: 24,
    fontWeight: FontWeight.bold,
    color: Color(0xFF212121),
  );

  static const bodyStyle = TextStyle(
    fontSize: 14,
    color: Color(0xFF616161),
  );

  // Padding & Margin
  static const smallPadding = EdgeInsets.all(8);
  static const mediumPadding = EdgeInsets.all(16);
  static const largePadding = EdgeInsets.all(24);

  // Border Radius
  static const smallRadius = BorderRadius.all(Radius.circular(4));
  static const mediumRadius = BorderRadius.all(Radius.circular(8));
  static const largeRadius = BorderRadius.all(Radius.circular(16));

  // Box Decorations
  static final cardDecoration = BoxDecoration(
    color: Colors.white,
    borderRadius: mediumRadius,
    boxShadow: [
      BoxShadow(
        color: Colors.black.withOpacity(0.1),
        blurRadius: 8,
        offset: Offset(0, 2),
      ),
    ],
  );
}

// Использование
Container(
  padding: AppStyles.mediumPadding,
  decoration: AppStyles.cardDecoration,
  child: Text('Card', style: AppStyles.headingStyle),
)

Способ 3: Полнофункциональный Theme класс

Для сложных приложений с множеством тем:

class AppTheme {
  static ThemeData lightTheme(BuildContext context) {
    return ThemeData(
      useMaterial3: true,
      brightness: Brightness.light,
      colorScheme: ColorScheme.light(
        primary: Colors.blue,
        secondary: Colors.teal,
        surface: Colors.white,
        error: Colors.red,
      ),
      textTheme: _buildTextTheme(),
      appBarTheme: AppBarTheme(
        backgroundColor: Colors.white,
        elevation: 0,
        centerTitle: true,
      ),
    );
  }

  static ThemeData darkTheme(BuildContext context) {
    return ThemeData(
      useMaterial3: true,
      brightness: Brightness.dark,
      colorScheme: ColorScheme.dark(
        primary: Colors.blue[300],
        secondary: Colors.teal[300],
        surface: Colors.grey[900],
        error: Colors.red[400],
      ),
      textTheme: _buildTextTheme(),
    );
  }

  static TextTheme _buildTextTheme() {
    return TextTheme(
      displayLarge: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
      headlineLarge: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
      bodyLarge: TextStyle(fontSize: 16),
      bodyMedium: TextStyle(fontSize: 14),
      labelSmall: TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
    );
  }
}

// Использование
MaterialApp(
  theme: AppTheme.lightTheme(context),
  darkTheme: AppTheme.darkTheme(context),
  themeMode: ThemeMode.system,
)

Способ 4: Komponenty-based approach

Использование переиспользуемых компонентов:

class StyledButton extends StatelessWidget {
  final String label;
  final VoidCallback onPressed;
  final ButtonStyle style;

  const StyledButton({
    required this.label,
    required this.onPressed,
    this.style = ButtonStyle.primary,
  });

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colors = _getColorScheme(style);

    return ElevatedButton(
      style: ElevatedButton.styleFrom(
        backgroundColor: colors.bgColor,
        foregroundColor: colors.textColor,
        padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(8),
        ),
      ),
      onPressed: onPressed,
      child: Text(label, style: theme.textTheme.labelLarge),
    );
  }

  _ColorScheme _getColorScheme(ButtonStyle style) {
    return switch (style) {
      ButtonStyle.primary => _ColorScheme(
        bgColor: Colors.blue,
        textColor: Colors.white,
      ),
      ButtonStyle.secondary => _ColorScheme(
        bgColor: Colors.grey[200],
        textColor: Colors.black,
      ),
      ButtonStyle.danger => _ColorScheme(
        bgColor: Colors.red,
        textColor: Colors.white,
      ),
    };
  }
}

enum ButtonStyle { primary, secondary, danger }

class _ColorScheme {
  final Color bgColor;
  final Color textColor;
  _ColorScheme({required this.bgColor, required this.textColor});
}

// Использование
StyledButton(
  label: 'Click me',
  onPressed: () {},
  style: ButtonStyle.primary,
)

Способ 5: Использование Riverpod для темы

final themeModeProvider = StateProvider<ThemeMode>((ref) {
  return ThemeMode.system;
});

final appThemeProvider = Provider<ThemeData>((ref) {
  final themeMode = ref.watch(themeModeProvider);
  return themeMode == ThemeMode.dark
      ? AppTheme.darkTheme()
      : AppTheme.lightTheme();
});

class MyApp extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final appTheme = ref.watch(appThemeProvider);
    final themeMode = ref.watch(themeModeProvider);

    return MaterialApp(
      theme: AppTheme.lightTheme(),
      darkTheme: AppTheme.darkTheme(),
      themeMode: themeMode,
    );
  }
}

Лучшие практики

  • Используйте цветовые переменные вместо хардкода
  • Централизуйте стили в одном месте
  • Создавайте переиспользуемые компоненты вместо дублирования
  • Документируйте стили с примерами
  • Тестируйте темы — светлую и тёмную
  • Используйте Material Design 3 где возможно
  • Избегайте глубокой вложенности в стилях

Правильная организация кода стайла улучшает читаемость, упрощает поддержку и ускоряет разработку.

Как организуешь код стайл в приложении? | PrepBro