Flutter 디지털 사이니지 앱 개발 가이드

1. 소개

1.1. 애플리케이션의 목적

본 문서는 Android 및 Android TV 플랫폼에서 작동하는 디지털 사이니지 클라이언트 애플리케이션을 Flutter를 통해 개발하는 과정을 안내한다. 이 애플리케이션은 서버 환경 및 PostgreSQL 데이터베이스와 통신하여 동적 콘텐츠(이미지 및 비디오)를 로컬에 다운로드하고, 이를 슬라이드쇼 형태로 재생하며, 주기적으로 콘텐츠를 갱신하는 것을 목표로 한다.

1.2. 핵심 기술 개요

개발에는 다음과 같은 핵심 기술이 사용된다:

  • Flutter: Google에서 개발한 UI 툴킷으로, 단일 코드베이스를 사용하여 Android, iOS, 웹, 데스크톱 등 다양한 플랫폼을 위한 네이티브 애플리케이션을 구축할 수 있다. 본 프로젝트에서는 Android (일반 OS) 및 Android TV OS용 클라이언트 개발에 활용된다.
  • Dart: Flutter 애플리케이션 개발에 사용되는 객체 지향 프로그래밍 언어이다.
  • Android (Standard & TV OS): 애플리케이션이 배포될 대상 운영체제이다. 특히 Android TV OS의 특성을 고려한 UI/UX 및 네비게이션 구현이 중요하다.
  • PostgreSQL: 백엔드 데이터베이스 시스템으로, 클라이언트 애플리케이션은 이 데이터베이스에 저장된 콘텐츠 메타데이터를 서버 API를 통해 조회하고 활용한다.

1.3. 가이드 구조

본 가이드는 프로젝트 설정부터 시작하여 각 기능별 구현 방법, 그리고 Android TV와 같은 특정 플랫폼 환경에서의 고려 사항을 단계별로 상세히 설명한다. 주요 내용은 다음과 같다:

  • 핵심 애플리케이션 설정: Flutter 프로젝트 초기화 및 Android Manifest 파일의 필수 구성 요소를 다룬다.
  • 기능 구현 가이드: 부팅 시 자동 실행, 전체 화면 모드, 사용자 인증, 콘텐츠 동기화 및 로컬 캐싱, 미디어 슬라이드쇼 등 요구되는 각 기능을 상세히 안내한다.
  • Android TV 특정 고려 사항: Android TV 환경에서의 UI/UX 디자인 원칙, D-패드 네비게이션 및 포커스 관리 기법을 심층적으로 논의한다.
  • 백엔드 고려 사항 (간략히): Flutter 클라이언트와 상호작용할 PHP/PostgreSQL 백엔드의 API 엔드포인트 및 데이터베이스 스키마에 대한 기본적인 사항을 언급한다.
  • 결론 및 모범 사례 요약: 주요 과제와 해결책을 요약하고, 테스트 및 보안 관련 권장 사항을 제시한다.

2. 핵심 애플리케이션 설정

2.1. Flutter 프로젝트 초기화

Flutter 개발 환경(Flutter SDK, Android Studio 또는 VS Code 등의 IDE)이 사전에 구축되어 있다는 가정 하에, 다음 명령어를 사용하여 새로운 Flutter 프로젝트를 생성한다:

flutter create digital_signage_app

이 명령어는 digital_signage_app이라는 이름의 Flutter 프로젝트 기본 구조를 생성한다.

2.2. 필수 Android Manifest 구성 (android/app/src/main/AndroidManifest.xml)

애플리케이션의 올바른 작동과 특정 기능 활성화를 위해 AndroidManifest.xml 파일에 몇 가지 중요한 설정을 추가해야 한다.

인터넷 권한

서버 통신 및 파일 다운로드를 위해 필수적이다.

<uses-permission android:name="android.permission.INTERNET" />

이 권한은 애플리케이션이 네트워크에 접근하여 이미지/비디오 목록 및 실제 파일을 다운로드하는 데 기본적으로 요구된다.

저장 공간 권한

파일을 로컬 저장소에 다운로드하고 관리하기 위해서는 저장 공간 접근 권한이 필요하다. Android 버전별로 저장 공간 접근 방식에 차이가 있으므로 이를 고려해야 한다.

Android 10 (API 레벨 29) 미만 버전에서는 일반적으로 외부 저장소에 대한 광범위한 접근을 위해 다음 권한이 사용되었다:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Android 10 이상에서는 Scoped Storage 모델이 도입되어 앱이 외부 저장소에 접근하는 방식이 변경되었다. 앱별 디렉토리(app-specific directories)에 파일을 저장하는 경우, 해당 앱의 파일에 대해서는 명시적인 저장 공간 권한이 필요하지 않을 수 있다. path_provider와 같은 플러그인은 이러한 앱별 디렉토리 경로를 얻는 데 사용된다.

그러나 flutter_downloader와 같은 다운로드 관리 플러그인이 공유 저장 공간을 사용하거나 특정 기능을 위해 더 넓은 접근 권한을 요구하는 경우, 여전히 위 권한들이 필요할 수 있으며, 런타임에 사용자에게 권한을 요청해야 한다. permission_handler 패키지는 런타임 권한 요청을 처리하는 표준적인 방법이다.

2.3. Android TV 특정 Manifest 구성 (android/app/src/main/AndroidManifest.xml)

Android TV에서 애플리케이션이 올바르게 인식되고 실행되도록 하려면 다음과 같은 TV 전용 설정을 Manifest 파일에 추가해야 한다.

Leanback Launcher 인텐트 필터

이 설정은 앱이 Android TV 홈 화면에 표시되도록 하는 데 필수적이다.

<application ...>
    <activity ...>
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
            <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
        </intent-filter>
    </activity>
</application>

CATEGORY_LEANBACK_LAUNCHER 인텐트 필터는 앱이 TV용으로 활성화되었음을 식별하고 Google Play에서 TV 앱으로 인식하도록 한다.

Leanback 지원 선언

앱이 Leanback UI(TV 환경)용으로 설계되었음을 나타낸다.

<uses-feature android:name="android.software.leanback" android:required="false" />

android:required="false" 속성은 일반 Android와 Android TV 모두에서 작동하는 앱을 개발할 때 중요하다.

터치스크린 불필요 선언

TV와의 상호작용은 주로 D-패드를 통해 이루어진다.

<uses-feature android:name="android.hardware.touchscreen" android:required="false" />

이 설정은 앱이 터치 입력 없이 탐색 가능함을 나타낸다.

Android TV용 배너

TV 홈 화면에서 앱을 나타내는 데 사용되는 배너 이미지이다.

<application
    ...
    android:banner="@drawable/your_tv_banner" >
   ...
</application>

your_tv_bannerres/drawable 디렉토리에 위치한 배너 이미지 파일의 이름으로 대체해야 한다.

3. 기능 구현 가이드

3.1. 부팅 시 자동 실행

애플리케이션(또는 콘텐츠 갱신을 위한 백그라운드 서비스)은 Android 장치(일반 또는 TV)가 부팅될 때 자동으로 시작되어야 한다.

핵심 Android 메커니즘

이는 android.intent.action.BOOT_COMPLETED 시스템 브로드캐스트를 수신하는 BroadcastReceiver를 사용하여 구현된다.

권한

AndroidManifest.xmlandroid.permission.RECEIVE_BOOT_COMPLETED 권한이 필요하다.

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

BroadcastReceiver 구현 (Kotlin 예제)

package com.example.digital_signage_app // 앱의 패키지 이름으로 변경

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log

class StartupReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
            Log.d("StartupReceiver", "Boot completed, attempting to start service or activity.")
            // 옵션 1: Flutter 액티비티 시작
            // val activityIntent = Intent(context, MainActivity::class.java).apply {
            //     flags = Intent.FLAG_ACTIVITY_NEW_TASK
            // }
            // context.startActivity(activityIntent)
            
            // 옵션 2: Flutter 백그라운드 서비스 시작 (플러그인 사용 권장)
            // 예: flutter_background_service 또는 workmanager
            // 특정 플러그인 문서를 참조하여 부팅 시 자동 시작 설정.
        }
    }
}

AndroidManifest.xml에 등록

<application ...>
    <receiver android:name=".StartupReceiver" android:enabled="true" android:exported="false">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
   ...
</application>

flutter_background_service, android_alarm_manager_plus, workmanager와 같은 플러그인을 활용하면 부팅 시 자동 실행 및 백그라운드 작업 관리를 더 쉽게 구현할 수 있다.

표: 부팅 완료 플러그인 비교
기능 flutter_background_service android_alarm_manager_plus workmanager 비고
부팅 시 설정 용이성 autoStartOnBoot: true 설정으로 비교적 간편 RebootBroadcastReceiver 내장, 자동 재스케줄링 시스템 재부팅 간 작업 지속 지원 각 플러그인 문서 참조 필요
Flutter 엔진 초기화 플러그인 내부 처리 플러그인 내부 처리 플러그인 내부 처리 개발자가 직접 처리할 필요 없음
작업 유형 일회성, 주기적, 포그라운드 서비스 일회성, 정확한 시간의 주기적 알람 (Android 전용) 지연 가능, 제약 조건 기반, 일회성, 주기적 작업 android_alarm_manager_plus는 정밀한 타이밍에 강점
iOS 지원 제한적 (Background Fetch 사용) 지원 안 함 지원 (Background Fetch/Processing 사용) iOS 백그라운드 실행은 제약이 많음

3.2. 전체 화면 모드

애플리케이션은 시스템 바(상태 바, 네비게이션 바)를 숨기고 전체 화면을 차지해야 한다. Flutter의 SystemChrome API를 사용한다.

import 'package:flutter/services.dart';

void enableFullScreenMode() {
  SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
  // 또는 이전 방식:
  // SystemChrome.setEnabledSystemUIOverlays([]); // 모든 오버레이 숨김
}

SystemUiMode.immersiveSticky는 사용자가 화면 가장자리에서 스와이프하면 시스템 바가 잠시 나타났다가 다시 사라지는 지속적인 전체 화면 모드이다. 일반적으로 main() 함수 또는 메인 앱 위젯의 initState에서 호출된다.

3.3. 사용자 인증

3.3.1. 로그인 UI 구축

표준 Flutter 위젯(Scaffold, Column, TextField, ElevatedButton, Form)을 사용하여 사용자 이름/ID 및 비밀번호 입력 필드와 로그인 버튼으로 구성된 UI를 만든다. TextEditingController로 입력 값을 가져오고 유효성 검사를 구현한다.

3.3.2. 로그인 자격 증명/토큰 보안 저장

성공적인 로그인 후 서버는 세션 토큰(예: JWT)을 반환해야 한다. flutter_secure_storage 패키지를 사용하여 이 토큰을 안전하게 저장한다.

import 'package:flutter_secure_storage/flutter_secure_storage.dart';

final storage = FlutterSecureStorage();

// 토큰 저장
await storage.write(key: 'auth_token', value: receivedToken);

// 토큰 읽기
String? token = await storage.read(key: 'auth_token');

이 패키지는 iOS의 Keychain과 Android의 KeyStore 기반 AES 암호화를 사용한다.

3.3.3. 자동 로그인 로직 구현

앱 시작 시 flutter_secure_storage에 저장된 토큰을 확인한다.

String? token = await storage.read(key: 'auth_token');
if (token != null && token.isNotEmpty) {
  // 서버로 토큰 유효성 검사 (선택 사항이지만 권장)
  // 유효하면 메인 앱 화면으로 이동.
} else {
  // 로그인 화면으로 이동.
}

만료되거나 유효하지 않은 토큰을 정상적으로 처리하여 로그인 화면으로 리디렉션해야 한다.

3.4. 콘텐츠 가져오기 및 로컬 캐싱 (이미지/비디오)

3.4.1. 백엔드로 HTTP 요청하기

dio 또는 http 패키지를 사용하여 백엔드 API에 파일 메타데이터(URL, 파일 이름, 버전 등) 목록을 요청한다. 인증 토큰을 요청 헤더에 포함시킨다.

// dio 패키지 사용 예시
import 'package:dio/dio.dart';

Future fetchFileList() async {
  Dio dio = Dio();
  String? token = await storage.read(key: 'auth_token'); // flutter_secure_storage 사용

  try {
    Response response = await dio.get(
      'YOUR_API_ENDPOINT/files',
      options: Options(
        headers: {
          'Authorization': 'Bearer $token',
        },
      ),
    );
    // response.data 처리 (파일 메타데이터 목록)
    print(response.data);
  } catch (e) {
    print('Error fetching file list: $e');
  }
}

3.4.2. 로컬 저장소로 파일 다운로드

flutter_downloader 패키지를 사용하여 파일을 백그라운드에서 다운로드하고, path_provider를 사용하여 적절한 로컬 저장소 경로를 얻는다.

import 'package:path_provider/path_provider.dart';
import 'package:flutter_downloader/flutter_downloader.dart';

Future downloadFile(String url, String fileName) async {
  final directory = await getApplicationDocumentsDirectory();
  final localPath = '${directory.path}/$fileName';

  final taskId = await FlutterDownloader.enqueue(
    url: url,
    savedDir: directory.path,
    fileName: fileName,
    showNotification: true, // 알림 표시 여부
    openFileFromNotification: true, // 알림 클릭 시 파일 열기 여부
  );
}

// main.dart에 FlutterDownloader 초기화 필요
// void main() async {
//   WidgetsFlutterBinding.ensureInitialized();
//   await FlutterDownloader.initialize(
//     debug: true // 디버그 로그 활성화
//   );
//   runApp(MyApp());
// }

flutter_downloader 사용 시 네이티브 설정(Android Manifest, iOS Info.plist)이 필요할 수 있다.

3.4.3. 주기적 새로고침 예약

workmanager 또는 android_alarm_manager_plus (Android 전용) 패키지를 사용하여 30분마다 파일 목록을 가져오고 로컬 파일을 업데이트하는 백그라운드 작업을 예약한다.

// workmanager 사용 예시
import 'package:workmanager/workmanager.dart';

void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) {
    print("Native called background task: $task"); // 실제 콘텐츠 새로고침 로직 호출
    // fetchFileList() 및 동기화 로직 수행
    return Future.value(true);
  });
}

void schedulePeriodicRefresh() {
  Workmanager().initialize(
    callbackDispatcher,
    isInDebugMode: true,
  );
  Workmanager().registerPeriodicTask(
    "contentRefreshTaskUniqueName", // 고유 이름
    "contentRefreshTask", // 작업 이름
    frequency: Duration(minutes: 30),
    constraints: Constraints(
      networkType: NetworkType.connected,
    ),
  );
}

3.5. 선택적 파일 동기화

주기적 새로고침 시, 로컬 파일 목록과 서버 파일 목록(메타데이터에 버전/타임스탬프 포함)을 비교한다. 새 파일이나 수정된 파일만 다운로드하고, 서버 목록에 없는 로컬 파일은 삭제한다.

로직 개요:

  1. 서버에서 최신 파일 메타데이터 목록을 가져온다.
  2. 로컬에 저장된 파일 메타데이터 목록(또는 실제 파일 정보)을 가져온다.
  3. 두 목록을 비교하여 다음을 식별한다:
    • 다운로드할 파일: 서버에는 있지만 로컬에는 없거나, 로컬 버전보다 최신인 파일.
    • 삭제할 파일: 로컬에는 있지만 서버 목록에는 없는 파일.
  4. 식별된 파일들을 다운로드하거나 삭제한다.

파일 내용의 해시(예: MD5, SHA256)를 사용하면 타임스탬프보다 더 정확한 변경 감지가 가능하다.

3.6. 로컬 미디어 슬라이드쇼

다운로드한 이미지와 비디오를 자동으로 순환 재생한다. carousel_slider (이미지/위젯 캐러셀) 및 video_player (비디오 재생) 패키지를 사용할 수 있다.

혼합 재생 목록 관리:

  • 로컬 파일 경로 및 유형(이미지/비디오) 목록을 유지한다.
  • PageView 또는 AnimatedSwitcher를 사용하여 현재 항목을 표시한다.
  • PageViewitemBuilder는 파일 유형에 따라 Image.file() 또는 VideoPlayer 위젯을 반환한다.
  • VideoPlayerController의 수명 주기(초기화, 재생, 폐기)를 신중하게 관리한다.

자동 전환:

  • 이미지: Timer를 사용하여 일정 시간 후 다음 항목으로 전환한다.
  • 비디오: VideoPlayerController의 리스너를 사용하여 재생 완료 시 다음 항목으로 전환한다.

AnimatedSwitcher는 다른 유형의 위젯 간 전환(예: 이미지에서 비디오로)을 부드럽게 처리하는 데 유용할 수 있다.

4. Android TV 특정 고려 사항

4.1. TV용 UI/UX 모범 사례

  • 10피트 UI: 멀리서 보는 것을 고려하여 큰 글꼴, 선명한 시각 자료, 높은 대비를 사용한다.
  • 간단한 네비게이션: D-패드 네비게이션은 직관적이고 예측 가능해야 한다.
  • 포커스 하이라이트: 현재 포커스된 요소를 명확하게 표시한다.
  • 레이아웃: 가로 방향을 주로 사용하며, 콘텐츠 탐색에는 그리드 레이아웃이 일반적이다.
  • 혼잡함 피하기: UI를 깔끔하게 유지하고 콘텐츠에 집중한다.

4.2. D-패드 네비게이션 및 포커스 관리

Flutter의 포커스 시스템 (FocusNode, FocusScope, FocusTraversalGroup, FocusTraversalPolicy)을 활용하여 D-패드 네비게이션을 구현한다.

핵심 개념

  • FocusNode: UI에서 포커스 가능한 요소를 나타낸다.
  • ShortcutsActions 위젯: 키 이벤트(예: D-패드 선택)를 IntentAction에 매핑한다.
  • RawKeyboardListener: 원시 키 이벤트에 대한 하위 수준 액세스를 제공한다.

포커스 순서 관리, 포커스 하이라이트 사용자 정의, 포커스 디버깅이 중요하다. 디지털 사이니지 앱의 주 슬라이드쇼 뷰는 복잡한 D-패드 네비게이션이 필요 없을 수 있지만, 설정 화면 등에서는 필수적이다.

표: 포커스 관리 기법
기법 핵심 Flutter 클래스/위젯 사용 사례 비고
기본 포커스 Focus, FocusNode 위젯을 포커스 가능하게 만들기 FocusNode는 상태를 유지하고 프로그래밍 방식 제어를 허용한다.
프로그래밍 방식 포커스 FocusNode.requestFocus(), FocusScope.of(context).requestFocus() 특정 위젯으로 포커스 동적 이동 버튼 클릭, 유효성 검사 실패 시 등에 사용된다.
포커스 순서 FocusTraversalGroup, FocusTraversalPolicy (예: OrderedTraversalPolicy, ReadingOrderTraversalPolicy), FocusOrder D-패드 네비게이션 순서 정의 TV UI에서 예측 가능한 네비게이션에 매우 중요하다.
키 이벤트 처리 Shortcuts, Actions, LogicalKeyboardKey, ActivateIntent, RawKeyboardListener D-패드 입력(선택, 방향키)에 응답 Shortcuts/Actions는 의미론적이며, RawKeyboardListener는 하위 수준이다.
시각적 하이라이트 Focus.onFocusChange, FocusNode.hasFocus, FocusThemeData, MaterialStateProperty 포커스된 위젯을 시각적으로 강조 사용자에게 현재 선택된 항목을 명확하게 보여준다.

5. 백엔드 고려 사항 (간략히 - PHP/PostgreSQL)

Flutter 애플리케이션의 기능은 백엔드 API와 밀접하게 연결되어 있으므로, 초기 단계에서 API 계약을 명확하게 정의하는 것이 중요하다.

API 엔드포인트 예시

데이터베이스 (PostgreSQL) 스키마 예시

PHP 백엔드는 PostgreSQL에 대한 인증을 처리하고 인증된 사용자를 기반으로 files 테이블을 쿼리한다. 파일 접근 보안은 백엔드의 책임이다.

6. 결론 및 모범 사례 요약

본 가이드는 Flutter를 사용하여 Android 및 Android TV용 디지털 사이니지 애플리케이션을 개발하는 데 필요한 핵심 기능과 고려 사항을 다루었다. 듀얼 플랫폼 개발의 주요 과제는 각 플랫폼의 특성을 이해하고 이에 맞는 UI/UX를 제공하는 것이다.

주요 모범 사례

7. 참고 자료

본 가이드에서 언급된 기술 및 패키지에 대한 공식 문서 및 유용한 자료들입니다.

  1. Fetch data from the internet | Flutter
  2. Flutter Download File from URL: A Step-by-Step Guide - DhiWise
  3. Flutter_Downloader Tutorial: Implement File Downloads in Flutter ...
  4. Read and write files | Flutter
  5. How to Manage App Permissions in Flutter - F22 Labs
  6. permission_handler | Flutter package - Pub.dev
  7. Create and run a TV app | Android TV | Android Developers
  8. flutter_notification_listener - Dart API docs - Pub.dev (Note: This seems less relevant to boot completion directly, original document might have a context.)
  9. Auto-launch issue with Flutter application - Google Play Help
  10. Broadcasts overview | Background work | Android Developers
  11. Flutter Run Background Service Even After App is Killed - Medium
  12. android_alarm_manager_plus | Flutter package - Pub.dev
  13. Background processes | Flutter
  14. flutter_background_service | Flutter package - Pub.dev
  15. `flutter_background_service` stops only in release mode - GitHub Issue
  16. flutter_background_service example | Flutter package - Pub.dev
  17. Flutter App Development: Scheduling Background Task - Codeclever
  18. Mastering Background Tasks in Flutter with Workmanager - DhiWise
  19. How to Enable or Disable Full Screen Mode in Flutter App - Flutter Stuff
  20. Can you hide the status bar completely? (Android) - Reddit
  21. Layout tutorial - Flutter Documentation
  22. Flutter – Design Login Page UI | GeeksforGeeks
  23. flutter_secure_storage | Flutter package - pub.dev
  24. Storing Data in Secure Storage in Flutter | Blog | Digital.ai
  25. Flutter – Fetching List of Data From API Through Dio | GeeksforGeeks
  26. Flutter Dio Complete Guide - Dbestech
  27. Flutter – Fetch Data From REST APIs | GeeksforGeeks
  28. sync_client 1.2.2 | Flutter package - Pub.dev
  29. diffutil_dart | Dart package - pub.dev
  30. Dart - Compare List of Objects - Piyush Agarwal
  31. collection | Dart package - Pub.dev
  32. Set difference method - Dart API
  33. diffutil_dart example | Dart package - Pub.dev
  34. carousel_slider | Flutter package - Pub.dev
  35. Flutter Video Streaming and Player - VdoCipher
  36. Embed a video player in your Flutter application | Mux
  37. Layout | Flutter
  38. How to play video and image in a list Flutter - Stack Overflow
  39. Flutter: How to create a slider with images and videos - Stack Overflow
  40. Build an Image/Video Slider in Flutter - Dev.to
  41. AnimatedSwitcher class - Flutter API
  42. Awesome Flutter Carousel in 7 Minutes - YouTube (Original link might be dead/redirected)
  43. AnimatedSwitcher (Flutter Widget of the Week) - YouTube (Original link might be dead/redirected)
  44. material library - Flutter API
  45. Animation and motion widgets - Flutter Documentation
  46. Smart TV App Development Best Practices - EITBiz
  47. TV navigation - Android Developers
  48. FocusHighlightStrategy enum - Flutter API
  49. FocusHighlightMode enum - Flutter API
  50. Understanding focus - Flutter Documentation
  51. Focus class - Flutter API
  52. hasFocus property - FocusNode class - Flutter API
  53. Focus and text fields | Flutter
  54. skipTraversal property - FocusNode class - Flutter API
  55. descendantsAreFocusable property - FocusNode class - Flutter API
  56. canRequestFocus property - FocusNode class - Flutter API
  57. focusNode property - Focus class - Flutter API
  58. debugLabel property - FocusNode class - Flutter API
  59. Actions and Shortcuts - Flutter Documentation
  60. requestFocus method - FocusScope class - Flutter API
  61. setFirstFocus method - FocusScopeNode class - Flutter API
  62. autofocus method - FocusScopeNode class - Flutter API
  63. focusedChild property - FocusScope class - Flutter API
  64. FocusTraversalGroup class - Flutter API
  65. FocusOrder class - Flutter API
  66. FocusTraversalPolicy class - Flutter API
  67. OrderedTraversalPolicy class - Flutter API
  68. WidgetOrderTraversalPolicy class - Flutter API
  69. ReadingOrderTraversalPolicy class - Flutter API
  70. FocusableActionDetector class - Flutter API
  71. Shortcuts class - Flutter API
  72. CallbackShortcuts class - Flutter API
  73. Intent class - Flutter API
  74. Actions class - Flutter API
  75. HardwareKeyboard class - Flutter API
  76. LogicalKeyboardKey class - Flutter API
  77. onKey property - RawKeyboardListener class - Flutter API
  78. keyboard property - ServicesBinding mixin - Flutter API
  79. autofocus property - Focus class - Flutter API
  80. RequestFocusAction class - Flutter API
  81. descendantsAreFocusable property - FocusableActionDetector - Flutter API
  82. onFocusChange property - Focus class - Flutter API
  83. FocusHighlightBehavior enum - Flutter API
  84. overlayColor property - FocusThemeData - Flutter API
  85. MaterialStateProperty class - Flutter API
  86. addHighlightStrategy method - FocusManager - Flutter API
  87. paint method - FocusHighlightStrategy - Flutter API
  88. automatic property - FocusHighlightStrategy - Flutter API
  89. onShowFocusHighlight property - FocusableActionDetector - Flutter API