Flutter Dynamically Loading Widgets

Flutter에서 동적 위젯 로딩(Dynamically Loading Widgets in Flutter)

Flutter에서는 기본적으로 정적으로 컴파일된 언어(Dart)를 사용하기 때문에, JavaScript처럼 동적으로 코드를 로딩하여 실행하는 기능이 제한적입니다. 하지만 몇 가지 방법을 활용하면 동적으로 위젯을 생성하고 실행할 수 있습니다.


1. Map<String, WidgetBuilder>을 활용한 동적 위젯 생성

Flutter에서는 Map<String, WidgetBuilder>를 사용하여 특정 문자열 키로 위젯을 찾고 실행할 수 있습니다.

예제 코드

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // 동적으로 위젯을 매핑하는 Map
  final Map<String, WidgetBuilder> widgetRegistry = {
    'home': (context) => HomeScreen(),
    'profile': (context) => ProfileScreen(),
  };

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("Dynamic Widget Loader")),
        body: DynamicWidgetLoader(widgetRegistry: widgetRegistry),
      ),
    );
  }
}

class DynamicWidgetLoader extends StatefulWidget {
  final Map<String, WidgetBuilder> widgetRegistry;
  const DynamicWidgetLoader({required this.widgetRegistry, Key? key}) : super(key: key);

  @override
  _DynamicWidgetLoaderState createState() => _DynamicWidgetLoaderState();
}

class _DynamicWidgetLoaderState extends State<DynamicWidgetLoader> {
  String selectedWidget = 'home';

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        DropdownButton<String>(
          value: selectedWidget,
          items: widget.widgetRegistry.keys
              .map((key) => DropdownMenuItem(value: key, child: Text(key)))
              .toList(),
          onChanged: (value) {
            setState(() {
              selectedWidget = value!;
            });
          },
        ),
        Expanded(
          child: widget.widgetRegistry[selectedWidget]!(context),
        ),
      ],
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(child: Text("Home Screen"));
  }
}

class ProfileScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(child: Text("Profile Screen"));
  }
}

설명

  • Map<String, WidgetBuilder>를 사용하여 키(String)로 위젯을 등록합니다.
  • DropdownButton으로 사용자가 선택한 위젯을 불러와 동적으로 변경합니다.

2. Reflection(반사)을 이용한 동적 위젯 로딩

Dart는 기본적으로 Reflection(반사)을 지원하지 않지만, dart:mirrors 패키지를 이용하면 클래스 이름을 문자열로 받아 인스턴스를 생성할 수 있습니다.

⚠️ 하지만 dart:mirrors는 Flutter에서 정식 지원되지 않으므로, json_serializable이나 code generation 같은 대체 방식을 사용하는 것이 좋습니다.

import 'dart:mirrors';

class HomeScreen {
  void show() => print("HomeScreen Loaded");
}

void main() {
  String className = "HomeScreen";  // 동적으로 로딩할 클래스 이름
  var mirrorSystem = currentMirrorSystem();
  var library = mirrorSystem.libraries.values.firstWhere((lib) => lib.declarations.containsKey(Symbol(className)));

  var classMirror = library.declarations[Symbol(className)] as ClassMirror;
  var instance = classMirror.newInstance(Symbol(''), []).reflectee;

  instance.show();  // "HomeScreen Loaded" 출력
}

주의사항

  • Flutter에서는 dart:mirrors를 지원하지 않음.
  • 웹 및 모바일 앱에서는 사용하기 어려움.

3. JSON을 이용한 동적 위젯 로딩

서버에서 JSON 데이터를 받아 동적으로 위젯을 생성하는 방법이 있습니다.

예제

JSON 데이터 (widget_config.json)

{
  "type": "container",
  "color": "0xFF42A5F5",
  "width": 200,
  "height": 100
}

Flutter 코드

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("JSON Dynamic Widget")),
        body: FutureBuilder(
          future: loadWidgetFromJson(),
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return CircularProgressIndicator();
            }
            if (snapshot.hasError) {
              return Text("Error loading widget");
            }
            return snapshot.data as Widget;
          },
        ),
      ),
    );
  }

  Future<Widget> loadWidgetFromJson() async {
    // 가정: JSON이 서버에서 로드됨
    String jsonString = '''
      {
        "type": "container",
        "color": "0xFF42A5F5",
        "width": 200,
        "height": 100
      }
    ''';
    Map<String, dynamic> jsonData = jsonDecode(jsonString);
    return parseWidget(jsonData);
  }

  Widget parseWidget(Map<String, dynamic> jsonData) {
    switch (jsonData["type"]) {
      case "container":
        return Container(
          width: jsonData["width"].toDouble(),
          height: jsonData["height"].toDouble(),
          color: Color(int.parse(jsonData["color"])),
        );
      default:
        return Text("Unknown Widget");
    }
  }
}

설명

  • JSON 데이터를 받아 위젯을 생성하는 방식
  • API에서 JSON을 받아 화면을 동적으로 구성할 수 있음

4. Code Generation(코드 생성)을 활용한 동적 로딩

build_runner와 같은 도구를 이용하여 코드 생성을 통해 동적으로 위젯을 로드하는 방식도 가능합니다.

  • json_serializable을 활용하여 JSON 데이터를 Dart 객체로 변환 후 위젯을 생성
  • flutter_dynamic_widget 패키지를 사용하여 JSON으로 UI 구성

결론(Conclusion)

방법설명장점단점
Map<String, WidgetBuilder>사전에 정의된 위젯을 동적으로 로드간단하고 효율적사전에 등록된 위젯만 가능
Reflection (dart:mirrors)문자열로 클래스를 로드하여 실행유연한 동적 로딩Flutter에서 지원되지 않음
JSON 기반 동적 위젯JSON 데이터를 기반으로 위젯을 생성서버 기반 UI 변경 가능미리 정의된 위젯 타입만 지원
Code Generation코드 생성을 통해 동적 UI 제공성능 최적화 가능초기 설정이 필요

Flutter는 기본적으로 정적으로 컴파일되므로 JavaScript처럼 자유롭게 코드를 불러와 실행할 수는 없지만, 위의 방법들을 활용하면 동적인 UI 구성이 가능합니다.

Leave a Reply

Your email address will not be published. Required fields are marked *