feat(android): Implement immersive mode for Flutter app

- Enable Flutter view to render behind system UI (status bar, navigation bar)
- Set transparent status bar and navigation bar colors in styles.xml
- Enable immersive sticky mode in Flutter main.dart
- MainActivity.kt: Set decor fits system windows to false for full-screen layout
This commit is contained in:
toki 2026-03-22 22:51:58 +09:00
parent 9b8946be49
commit cd86d34d5a
4 changed files with 26 additions and 4 deletions

View file

@ -1,6 +1,7 @@
package com.tokilabs.mattermost package com.tokilabs.mattermost
import android.os.Bundle import android.os.Bundle
import androidx.core.view.WindowCompat
import com.tokilabs.mattermost.helpers.DatabaseHelper import com.tokilabs.mattermost.helpers.DatabaseHelper
import com.tokilabs.mattermost.helpers.Network import com.tokilabs.mattermost.helpers.Network
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
@ -100,6 +101,8 @@ class MainActivity : FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
// 알림 탭으로 앱 진입 시 Intent 데이터를 Flutter로 전달 // 알림 탭으로 앱 진입 시 Intent 데이터를 Flutter로 전달
intent?.extras?.let { extras -> intent?.extras?.let { extras ->
val data = mutableMapOf<String, Any?>() val data = mutableMapOf<String, Any?>()

View file

@ -5,6 +5,8 @@
<!-- Show a splash screen on the activity. Automatically removed when <!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame --> the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item> <item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your This theme determines the color of the Android Window while your
@ -14,5 +16,7 @@
This Theme is only used starting with V2 of Flutter's Android embedding. --> This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar"> <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item> <item name="android:windowBackground">?android:colorBackground</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style> </style>
</resources> </resources>

View file

@ -5,6 +5,8 @@
<!-- Show a splash screen on the activity. Automatically removed when <!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame --> the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item> <item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your This theme determines the color of the Android Window while your
@ -14,5 +16,7 @@
This Theme is only used starting with V2 of Flutter's Android embedding. --> This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar"> <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item> <item name="android:windowBackground">?android:colorBackground</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style> </style>
</resources> </resources>

View file

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'services/mattermost_auth_service.dart'; import 'services/mattermost_auth_service.dart';
import 'services/push_notification_background.dart'; import 'services/push_notification_background.dart';
@ -9,9 +10,20 @@ import 'services/push_notification_service.dart';
final _navigatorKey = GlobalKey<NavigatorState>(); final _navigatorKey = GlobalKey<NavigatorState>();
final _pushService = PushNotificationService(); final _pushService = PushNotificationService();
Future<void> _applyFullscreenMode() async {
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
systemNavigationBarColor: Colors.transparent,
systemNavigationBarDividerColor: Colors.transparent,
));
}
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await _applyFullscreenMode();
await Firebase.initializeApp(); await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler); FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
@ -68,6 +80,9 @@ class _MyHomePageState extends State<MyHomePage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_applyFullscreenMode();
});
_notifSub = _pushService.onNotification.listen((data) { _notifSub = _pushService.onNotification.listen((data) {
if (data['type'] != 'message') return; if (data['type'] != 'message') return;
final message = data['message'] as String? ?? ''; final message = data['message'] as String? ?? '';
@ -97,10 +112,6 @@ class _MyHomePageState extends State<MyHomePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: const Center( body: const Center(
child: Text('NomadCode'), child: Text('NomadCode'),
), ),