Flutter로 iOS와 Android의 StatusBar를 어떻게 스타일링하나요?

질문

Flutter 애플리케이션에서 iOS와 Android의 상태 표시줄(StatusBar)을 커스터마이징하는 방법에 대해 설명해주세요. 색상, 투명도, 텍스트 색상을 변경하는 방법과 플랫폼별 차이점에 대해 알려주세요.

답변

Flutter에서 상태 표시줄(StatusBar)을 스타일링하는 것은 앱의 전체적인 디자인과 일관성을 유지하는 데 중요합니다. iOS와 Android는 상태 표시줄 처리 방식에 차이가 있지만, Flutter는 이러한 차이를 추상화하여 일관된 API를 제공합니다.

1. 기본 상태 표시줄 스타일링

1.1 SystemUiOverlayStyle 사용

Flutter에서 상태 표시줄을 스타일링하는 가장 일반적인 방법은 SystemUiOverlayStyle을 사용하는 것입니다:

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

void main() {
  // 앱 시작 시 상태 표시줄 스타일 설정
  SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
    statusBarColor: Colors.transparent, // 상태 표시줄 배경색 (Android)
    statusBarIconBrightness: Brightness.dark, // 상태 표시줄 아이콘 색상 (Android)
    statusBarBrightness: Brightness.light, // 상태 표시줄 아이콘 색상 (iOS)
  ));

  runApp(MyApp());
}

1.2 AppBar를 통한 간접 제어

AppBar 위젯을 사용할 때는 systemOverlayStyle 속성을 통해 상태 표시줄 스타일을 설정할 수 있습니다:

Scaffold(
  appBar: AppBar(
    title: Text('상태 표시줄 스타일링'),
    backgroundColor: Colors.blue,
    systemOverlayStyle: SystemUiOverlayStyle(
      statusBarColor: Colors.transparent,
      statusBarIconBrightness: Brightness.light, // Android에서 아이콘을 밝게(흰색)
      statusBarBrightness: Brightness.dark, // iOS에서 아이콘을 어둡게(검은색)
    ),
  ),
  body: Center(
    child: Text('상태 표시줄 스타일 테스트'),
  ),
)

1.3 AnnotatedRegion 위젯 사용

특정 화면에서만 스타일을 변경하려면 AnnotatedRegion 위젯을 사용할 수 있습니다:

AnnotatedRegion<SystemUiOverlayStyle>(
  value: SystemUiOverlayStyle(
    statusBarColor: Colors.transparent,
    statusBarIconBrightness: Brightness.dark,
    statusBarBrightness: Brightness.light,
  ),
  child: Scaffold(
    // 스캐폴드 내용
  ),
)

2. 플랫폼별 상태 표시줄 스타일링

2.1 Android 특화 스타일링

Android에서는 statusBarColorstatusBarIconBrightness가 주로 사용됩니다:

// Android에서 투명한 상태 표시줄과 어두운 아이콘
if (Platform.isAndroid) {
  SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
    statusBarColor: Colors.transparent,
    statusBarIconBrightness: Brightness.dark,
  ));
}

Android API 수준에 따른 차이를 처리하는 방법:

// Android API 레벨 확인 및 대응
if (Platform.isAndroid) {
  final androidInfo = await DeviceInfoPlugin().androidInfo;
  final sdkVersion = androidInfo.version.sdkInt;

  // Android 6.0 (API level 23) 이상
  if (sdkVersion >= 23) {
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
      statusBarColor: Colors.transparent,
      systemNavigationBarColor: Colors.black,
      statusBarIconBrightness: Brightness.dark,
      systemNavigationBarIconBrightness: Brightness.light,
    ));
  } else {
    // 이전 버전의 Android
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
      statusBarColor: Colors.black.withOpacity(0.4),
      systemNavigationBarColor: Colors.black,
    ));
  }
}

2.2 iOS 특화 스타일링

iOS에서는 statusBarBrightness가 상태 표시줄 콘텐츠 색상을 결정합니다:

// iOS에서 밝은 배경에 어두운 콘텐츠
if (Platform.isIOS) {
  SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
    statusBarBrightness: Brightness.light, // 밝은 배경에 어두운(검은색) 글자
  ));
}

iOS에서의 안전 영역(Safe Area) 처리:

// iOS의 상태 표시줄과 안전 영역 처리
if (Platform.isIOS) {
  return CupertinoPageScaffold(
    navigationBar: CupertinoNavigationBar(
      backgroundColor: Colors.transparent,
      border: Border.all(color: Colors.transparent),
      // iOS에서 투명한 상태 표시줄 구현
    ),
    child: SafeArea(
      child: Center(
        child: Text('iOS 스타일 상태 표시줄'),
      ),
    ),
  );
}

3. 상태 표시줄 색상 변경 예제

3.1 테마별 상태 표시줄 스타일 변경

앱의 테마에 따라 상태 표시줄 스타일을 변경하는 예제:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        brightness: Brightness.light,
        primarySwatch: Colors.blue,
        appBarTheme: AppBarTheme(
          systemOverlayStyle: SystemUiOverlayStyle(
            statusBarColor: Colors.transparent,
            statusBarIconBrightness: Brightness.dark,
            statusBarBrightness: Brightness.light,
          ),
        ),
      ),
      darkTheme: ThemeData(
        brightness: Brightness.dark,
        primarySwatch: Colors.blueGrey,
        appBarTheme: AppBarTheme(
          systemOverlayStyle: SystemUiOverlayStyle(
            statusBarColor: Colors.transparent,
            statusBarIconBrightness: Brightness.light,
            statusBarBrightness: Brightness.dark,
          ),
        ),
      ),
      themeMode: ThemeMode.system, // 시스템 테마 따름
      home: HomePage(),
    );
  }
}

3.2 동적으로 상태 표시줄 색상 변경

스크롤 위치나 사용자 작업에 따라 상태 표시줄 색상을 동적으로 변경하는 예제:

class DynamicStatusBarPage extends StatefulWidget {
  @override
  _DynamicStatusBarPageState createState() => _DynamicStatusBarPageState();
}

class _DynamicStatusBarPageState extends State<DynamicStatusBarPage> {
  bool _isDarkStatusBar = false;

  void _toggleStatusBarStyle() {
    setState(() {
      _isDarkStatusBar = !_isDarkStatusBar;

      SystemChrome.setSystemUIOverlayStyle(
        _isDarkStatusBar
            ? SystemUiOverlayStyle.dark.copyWith(
                statusBarColor: Colors.transparent,
              )
            : SystemUiOverlayStyle.light.copyWith(
                statusBarColor: Colors.transparent,
              ),
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('동적 상태 표시줄'),
        backgroundColor: _isDarkStatusBar ? Colors.white : Colors.blue,
        foregroundColor: _isDarkStatusBar ? Colors.black : Colors.white,
        elevation: 0,
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: _toggleStatusBarStyle,
          child: Text('상태 표시줄 색상 전환'),
        ),
      ),
    );
  }
}

4. 상태 표시줄 투명도 제어

4.1 완전 투명한 상태 표시줄 설정

상태 표시줄을 완전히 투명하게 만들고 콘텐츠가 그 아래로 확장되도록 설정:

class TransparentStatusBarPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 상태 표시줄 투명 설정
    SystemChrome.setSystemUIOverlayStyle(
      SystemUiOverlayStyle(
        statusBarColor: Colors.transparent,
        statusBarIconBrightness: Brightness.light,
        statusBarBrightness: Brightness.dark,
      ),
    );

    return Scaffold(
      // extendBodyBehindAppBar를 true로 설정하여 본문을 앱바 뒤로 확장
      extendBodyBehindAppBar: true,
      appBar: AppBar(
        backgroundColor: Colors.transparent,
        elevation: 0,
        title: Text('투명 상태 표시줄'),
      ),
      body: Container(
        // 전체 화면 배경 이미지
        decoration: BoxDecoration(
          image: DecorationImage(
            image: NetworkImage('https://picsum.photos/id/237/800/1600'),
            fit: BoxFit.cover,
          ),
        ),
        child: Center(
          child: Text(
            '투명한 상태 표시줄 예제',
            style: TextStyle(
              color: Colors.white,
              fontSize: 24,
              fontWeight: FontWeight.bold,
            ),
          ),
        ),
      ),
    );
  }
}

4.2 반투명 효과 만들기 (Android)

Android에서 반투명 상태 표시줄 효과 만들기:

// Android에서만 적용
if (Platform.isAndroid) {
  SystemChrome.setSystemUIOverlayStyle(
    SystemUiOverlayStyle(
      statusBarColor: Colors.black.withOpacity(0.3), // 반투명 검은색
      statusBarIconBrightness: Brightness.light,
    ),
  );
}

5. 상태 표시줄 제어 고급 기술

5.1 상태 표시줄 숨기기/표시하기

특정 화면에서 상태 표시줄을 완전히 숨기거나 다시 표시하는 기능:

// 상태 표시줄 숨기기
void hideStatusBar() {
  SystemChrome.setEnabledSystemUIMode(
    SystemUiMode.immersive,
  );
}

// 상태 표시줄 다시 표시하기
void showStatusBar() {
  SystemChrome.setEnabledSystemUIMode(
    SystemUiMode.edgeToEdge,
  );
}

// 사용 예시
class FullScreenPage extends StatefulWidget {
  @override
  _FullScreenPageState createState() => _FullScreenPageState();
}

class _FullScreenPageState extends State<FullScreenPage> {
  bool _isFullScreen = false;

  void _toggleFullScreen() {
    setState(() {
      _isFullScreen = !_isFullScreen;
      if (_isFullScreen) {
        hideStatusBar();
      } else {
        showStatusBar();
      }
    });
  }

  @override
  void dispose() {
    // 화면을 떠날 때 원래대로 복원
    showStatusBar();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GestureDetector(
        onTap: _toggleFullScreen,
        child: Container(
          color: Colors.black,
          child: Center(
            child: Text(
              _isFullScreen ? '탭하여 상태 표시줄 표시' : '탭하여 전체 화면 모드',
              style: TextStyle(color: Colors.white),
            ),
          ),
        ),
      ),
    );
  }
}

5.2 상태 표시줄 색상을 앱 콘텐츠에 맞게 자동 조정

스크롤 위치에 따라 자동으로 상태 표시줄 색상 변경하기:

class ScrollAwareStatusBarPage extends StatefulWidget {
  @override
  _ScrollAwareStatusBarPageState createState() => _ScrollAwareStatusBarPageState();
}

class _ScrollAwareStatusBarPageState extends State<ScrollAwareStatusBarPage> {
  late ScrollController _scrollController;
  Color _statusBarColor = Colors.transparent;
  Brightness _statusBarIconBrightness = Brightness.light;

  @override
  void initState() {
    super.initState();
    _scrollController = ScrollController();
    _scrollController.addListener(_onScroll);
  }

  void _onScroll() {
    final offset = _scrollController.offset;
    final maxOffset = 200.0; // 색상 변화의 기준점

    if (offset < maxOffset) {
      // 스크롤 위치에 따라 투명도 계산
      final opacity = (offset / maxOffset).clamp(0.0, 1.0);

      setState(() {
        // 배경색 변화
        _statusBarColor = Colors.white.withOpacity(opacity);

        // 아이콘 색상 변화 (투명도 50% 지점에서 전환)
        _statusBarIconBrightness = opacity > 0.5 ? Brightness.dark : Brightness.light;

        // 상태 표시줄 스타일 적용
        SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
          statusBarColor: _statusBarColor,
          statusBarIconBrightness: _statusBarIconBrightness,
          statusBarBrightness: _statusBarIconBrightness == Brightness.dark
              ? Brightness.light
              : Brightness.dark,
        ));
      });
    }
  }

  @override
  void dispose() {
    _scrollController.removeListener(_onScroll);
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBodyBehindAppBar: true,
      appBar: AppBar(
        backgroundColor: Colors.transparent,
        elevation: 0,
        title: Text('스크롤 기반 상태 표시줄'),
      ),
      body: CustomScrollView(
        controller: _scrollController,
        slivers: [
          SliverToBoxAdapter(
            child: Container(
              height: 300,
              decoration: BoxDecoration(
                image: DecorationImage(
                  image: NetworkImage('https://picsum.photos/800/600'),
                  fit: BoxFit.cover,
                ),
              ),
            ),
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (context, index) {
                return ListTile(
                  title: Text('항목 $index'),
                  subtitle: Text('스크롤하여 상태 표시줄 변화 확인'),
                );
              },
              childCount: 50,
            ),
          ),
        ],
      ),
    );
  }
}

6. 플랫폼별 특이점 및 주의사항

6.1 iOS의 특이사항

  1. Safe Area 고려: iOS에서는 상태 표시줄 아래 Safe Area를 고려해야 합니다.
  2. statusBarBrightness: iOS에서는 statusBarBrightness가 상태 표시줄 텍스트 색상을 결정합니다.
  3. 컬러 처리: iOS는 상태 표시줄 자체 색상(statusBarColor)을 직접 변경할 수 없으며, 배경이 상태 표시줄 아래로 확장됩니다.
// iOS에서 SafeArea 적절히 활용하기
if (Platform.isIOS) {
  return CupertinoPageScaffold(
    child: Column(
      children: [
        Container(
          color: Colors.blue,
          // SafeArea의 top만 true로 설정
          child: SafeArea(
            bottom: false,
            child: AppBar(
              backgroundColor: Colors.blue,
              title: Text('iOS 스타일'),
            ),
          ),
        ),
        Expanded(
          child: SafeArea(
            top: false, // 상단 Safe Area는 이미 처리했으므로 제외
            child: ListView.builder(
              itemCount: 50,
              itemBuilder: (context, index) => ListTile(
                title: Text('항목 $index'),
              ),
            ),
          ),
        ),
      ],
    ),
  );
}

6.2 Android의 특이사항

  1. API 수준 차이: Android 버전에 따라 상태 표시줄 처리 방식이 다릅니다.
  2. 네비게이션 바 고려: Android에서는 하단 네비게이션 바도 스타일링 고려가 필요합니다.
  3. 제조사별 차이: 일부 Android 제조사는 UI 오버레이를 변경할 수 있으며, 이는 상태 표시줄 스타일에 영향을 줄 수 있습니다.
// Android에서 네비게이션 바까지 스타일링하기
if (Platform.isAndroid) {
  SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
    // 상태 표시줄 설정
    statusBarColor: Colors.transparent,
    statusBarIconBrightness: Brightness.dark,

    // 네비게이션 바 설정
    systemNavigationBarColor: Colors.white,
    systemNavigationBarIconBrightness: Brightness.dark,
    systemNavigationBarDividerColor: Colors.transparent,
  ));
}

7. 모범 사례

7.1 일관된 앱 테마와의 통합

// ThemeData에 상태 표시줄 스타일 통합
ThemeData lightTheme = ThemeData(
  brightness: Brightness.light,
  primarySwatch: Colors.blue,
  appBarTheme: AppBarTheme(
    systemOverlayStyle: SystemUiOverlayStyle(
      statusBarColor: Colors.transparent,
      statusBarIconBrightness: Brightness.light,
      statusBarBrightness: Brightness.dark,
    ),
  ),
);

ThemeData darkTheme = ThemeData(
  brightness: Brightness.dark,
  primarySwatch: Colors.indigo,
  appBarTheme: AppBarTheme(
    backgroundColor: Colors.grey[900],
    systemOverlayStyle: SystemUiOverlayStyle(
      statusBarColor: Colors.transparent,
      statusBarIconBrightness: Brightness.light,
      statusBarBrightness: Brightness.dark,
    ),
  ),
);

7.2 화면 별 설정 관리

// Screen 또는 Widget 레벨에서 상태 표시줄 관리 클래스
class StatusBarManager {
  // 밝은 스타일 (어두운 아이콘)
  static void setLightStyle() {
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
      statusBarColor: Colors.transparent,
      statusBarIconBrightness: Brightness.dark,
      statusBarBrightness: Brightness.light,
    ));
  }

  // 어두운 스타일 (밝은 아이콘)
  static void setDarkStyle() {
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
      statusBarColor: Colors.transparent,
      statusBarIconBrightness: Brightness.light,
      statusBarBrightness: Brightness.dark,
    ));
  }

  // 투명 스타일
  static void setTransparentStyle({bool darkIcons = false}) {
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
      statusBarColor: Colors.transparent,
      statusBarIconBrightness: darkIcons ? Brightness.dark : Brightness.light,
      statusBarBrightness: darkIcons ? Brightness.light : Brightness.dark,
    ));
  }
}

// 사용 예시
class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  void initState() {
    super.initState();
    // 밝은 스타일 적용
    StatusBarManager.setLightStyle();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('홈 화면')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: StatusBarManager.setLightStyle,
              child: Text('밝은 스타일'),
            ),
            ElevatedButton(
              onPressed: StatusBarManager.setDarkStyle,
              child: Text('어두운 스타일'),
            ),
            ElevatedButton(
              onPressed: () => StatusBarManager.setTransparentStyle(),
              child: Text('투명 스타일'),
            ),
          ],
        ),
      ),
    );
  }
}

결론

Flutter에서 iOS와 Android의 상태 표시줄을 스타일링하는 것은 비교적 간단하지만, 플랫폼별 차이점을 이해하고 적절히 처리하는 것이 중요합니다. SystemUiOverlayStyle을 통해 상태 표시줄의 색상, 투명도, 아이콘 색상을 제어할 수 있으며, 이를 앱 테마와 통합하거나 화면별로 동적으로 변경할 수 있습니다.

특히 안전 영역(Safe Area)을 고려하고, 플랫폼별 특성을 반영하여 디자인하는 것이 중요합니다. 앱의 브랜딩과 UI/UX 일관성을 유지하면서도 각 플랫폼의 가이드라인을 존중하는 상태 표시줄 스타일링을 구현하면, 더 전문적이고 세련된 사용자 경험을 제공할 수 있습니다.

results matching ""

    No results matching ""