본문 바로가기
Flutter

[Flutter] MaterialApp을 statefulwidget에서 쓰면 좋은 경우

by 김무스비 2025. 1. 29.
728x90
반응형

MaterialApp은 Flutter 애플리케이션의 루트 위젯으로, 기본 설정 및 전역 상태를 관리하는 핵심 위젯입니다. MaterialApp을 통해 앱의 테마, 라우팅, 로컬화 등 다양한 설정을 관리할 수 있는데, 이건 주로 변경되는 경우가 적기 때문에 StatelessWidget으로 많이들 씁니다.(물론 StatefulWidget으로 쓴다고 문제가 생기는 건 아님, 에디터에서 convert해주면 정상 작동함)

하지만, 그럼에도 MaterialApp을 statefulwidget으로 쓰면 좋은 경우가 있는데요.

 


1. 동적 설정 변경

사용자가 앱 내에서 테마(라이트 모드/다크 모드/색상 테마)를 변경할 수 있도록 하려는 경우와  + 다국어 지원 등 동적으로 설정을 변경해야하는 경우 MaterialApp StatefulWidget으로 만들어 상태 관리가 가능합니다.

더보기
mport 'package:flutter/material.dart';
void main() {
  runApp(const AppState());
}
class AppState extends StatefulWidget {
  const AppState({super.key});
  @override
  _AppStateState createState() => _AppStateState();
}
class _AppStateState extends State<AppState> {
  int _selectedThemeIndex = 0;
  final List<ThemeData> _themes = [
    ThemeData.light(),
    ThemeData.dark(),
    ThemeData(
      primarySwatch: Colors.blue,
      primaryColor: Colors.blue,
      colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.blue)
          .copyWith(secondary: Colors.blueAccent),
    ),
    ThemeData(
      primarySwatch: Colors.green,
      primaryColor: Colors.green,
      colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.green)
          .copyWith(secondary: Colors.greenAccent),
    ),
    ThemeData(
      primarySwatch: Colors.red,
      primaryColor: Colors.red,
      colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.red)
          .copyWith(secondary: Colors.redAccent),
    ),
  ];
  void _selectTheme(int index) {
    setState(() {
      _selectedThemeIndex = index;
    });
  }
  @override
  Widget build(BuildContext context) {
    return MyApp(
      theme: _themes[_selectedThemeIndex],
      selectTheme: _selectTheme,
      selectedThemeIndex: _selectedThemeIndex,
    );
  }
}
class MyApp extends StatelessWidget {
  final ThemeData theme;
  final Function(int) selectTheme;
  final int selectedThemeIndex;
  const MyApp({
    super.key,
    required this.theme,
    required this.selectTheme,
    required this.selectedThemeIndex,
  });
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: theme,
      home: MyHomePage(
        selectTheme: selectTheme,
        selectedThemeIndex: selectedThemeIndex,
      ),
    );
  }
}
class MyHomePage extends StatelessWidget {
  final Function(int) selectTheme;
  final int selectedThemeIndex;
  const MyHomePage({
    super.key,
    required this.selectTheme,
    required this.selectedThemeIndex,
  });
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home Page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ToggleButtons(
              isSelected:
                  List.generate(5, (index) => index == selectedThemeIndex),
              onPressed: (index) {
                selectTheme(index);
              },
              children: const [
                Icon(Icons.wb_sunny),
                Icon(Icons.nightlight_round),
                Icon(Icons.circle, color: Colors.blue),
                Icon(Icons.circle, color: Colors.green),
                Icon(Icons.circle, color: Colors.red),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

 

2. 동적 라우팅 및 조건부 네비게이션

사용자 상태나 앱의 특정 조건에 따라 동적으로 라우팅 및 네비게이션을 처리해야 할 때 StatefulWidget을 쓰면 유용합니다.  MaterialApp에서 home 속성에 3항 연산자 쓰면 편하게 쓸 수 있겠죠?

더보기
 


import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool _isUserLoggedIn = false;

  void _login() {
    setState(() {
      _isUserLoggedIn = true;
    });
  }

  void _logout() {
    setState(() {
      _isUserLoggedIn = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Dynamic Routing Example',
      home: _isUserLoggedIn ? HomeScreen(logout: _logout) : LoginScreen(login: _login),
    );
  }
}

class LoginScreen extends StatelessWidget {
  final VoidCallback login;

  LoginScreen({required this.login});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Login'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: login,
          child: Text('Login'),
        ),
      ),
    );
  }
}

class HomeScreen extends StatelessWidget {
  final VoidCallback logout;

  HomeScreen({required this.logout});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: logout,
          child: Text('Logout'),
        ),
      ),
    );
  }
}

이상입니다.

읽어주셔서 감사합니다.

 
728x90
반응형