Skip to content

Fully Automated Flutter Package Integration for Flet #5230

@Muddassir-Farooq-official

Description

Duplicate Check

Describe the requested feature

We need a tool (fletpkg) that automates the integration of Flutter packages (from pub.dev) into Flet applications. The tool should handle all steps—downloading the package, parsing its Dart code, generating Python wrappers, creating custom Flet control classes, setting up dependencies, and generating a ready-to-run Flet app (main.py). The process should be seamless, requiring no manual intervention beyond running a single command.

Goals
Automation: Users should only need to run one command (e.g., fletpkg integrate <package_name>).
Comprehensive: The tool should handle all aspects of integration, including dependency management, code generation, and app setup.
Well-Structured: The generated code and project structure should follow best practices for Flet and Python.
User-Friendly: Even beginners should be able to use the tool without needing to understand Dart, Flutter, or Flet internals.
Architecture
The tool (fletpkg) will be a Python CLI script that performs the following steps:

Package Download: Fetch the specified Flutter package from pub.dev.
Dart Parsing: Analyze the package’s Dart code to extract public APIs (functions, classes, parameters, etc.).
Wrapper Generation: Generate a Python wrapper class that extends flet.Control and maps to the Dart APIs.
Dart-Python Bridge: Set up a WebSocket-based bridge to call Dart functions from Python.
Dependency Setup: Automatically configure pubspec.yaml and other dependencies.
App Generation: Create a main.py Flet app with a sample UI that demonstrates the package’s functionality.
Project Structure: Organize all generated files into a well-structured project.

flet_project/
├── flutter/
│ └── main.dart # Custom Flutter app with Dart-Python bridge
├── wrappers/
│ └── _wrapper.py # Generated Python wrapper class
├── pubspec.yaml # Flutter dependencies (e.g., url_launcher, flet)
├── main.py # Generated Flet app with sample UI
└── fletpkg.py # The CLI tool itself (can be separate)

Suggest a solution

Detailed Implementation Steps

Step 1: CLI Command Setup
Command: fletpkg integrate <package_name>
Example: fletpkg integrate url_launcher
Use Python’s argparse to create a CLI with a single integrate command.
The command should orchestrate the entire process.

Step 2: Package Download
Input: Package name (e.g., url_launcher).
Process:
Create a pubspec.yaml file in the project root with the package as a dependency:
yaml

name: flet_app
description: A Flet app with integrated Flutter package
environment:
  sdk: '>=2.18.0 <4.0.0'
dependencies:
  flet: ^0.23.0
  <package_name>: any

Run flutter pub get to download the package.
Locate the package in the global pub cache (e.g., ~\AppData\Local\Pub\Cache\hosted\pub.dev<package_name>-).

Step 3: Dart Parsing
Goal: Extract public functions, their parameters, and return types from the Dart code.

Process:
Traverse the package’s lib/ directory (e.g., url_launcher-/lib/).
Use a regex or Dart parser (e.g., analyzer package) to extract:
Public function names (e.g., launchUrl).
Parameter names and types (e.g., Uri url, LaunchOptions options).
Return types (e.g., Future).
Filter out private methods (starting with _) and non-functions (e.g., classes, variables).
Return a structured list of APIs, e.g.:
python

[
{"name": "launchUrl", "params": [{"name": "url", "type": "Uri"}], "return": "Future"},
{"name": "canLaunchUrl", "params": [{"name": "url", "type": "Uri"}], "return": "Future"}
]

Step 4: Generate Python Wrapper
Goal: Create a Python class that extends flet.Control and maps to the Dart APIs.
Process:
Generate a file wrappers/<package_name>_wrapper.py.
Create a class (e.g., UrlLauncher) with:
A constructor that initializes the Flet control.
A _call_dart method to send commands to Dart via WebSocket.
Methods for each parsed Dart function, mapping Python calls to Dart.
Example for url_launcher:
python

# wrappers/url_launcher_wrapper.py
import flet as ft
import json

class UrlLauncher(ft.Control):
    def __init__(self, **kwargs):
        super().__init__()
        self._package = "url_launcher"
        for k, v in kwargs.items():
            setattr(self, k, v)

    def _call_dart(self, method: str, params: dict):
        message = {
            "method": method,
            "params": params
        }
        self.page.client_storage.set("dart_call", json.dumps(message))
        self.page.pubsub.send_all("call_dart")

    def launchUrl(self, url: str, **kwargs):
        params = {"url": url, "kwargs": kwargs}
        self._call_dart("launchUrl", params)

    def canLaunchUrl(self, url: str, **kwargs):
        params = {"url": url, "kwargs": kwargs}
        self._call_dart("canLaunchUrl", params)

Step 5: Set Up the Dart-Python Bridge
Goal: Allow Python to call Dart functions via Flet’s WebSocket.
Process:
Create a flutter/main.dart file:
dart

import 'dart:convert';
import 'package:flet/flet.dart';
import 'package:flutter/material.dart';
import 'package:<package_name>/<package_name>.dart' as pkg;

void main() async {
await runAppWithFlet(
(pageClient) async {
return MaterialApp(
home: FletApp(pageClient: pageClient),
);
},
onMessage: (message, sendResponse) async {
if (message["topic"] == "call_dart") {
String? dartCall = pageClient.clientStorage.get("dart_call");
if (dartCall != null) {
Map<String, dynamic> callData = jsonDecode(dartCall);
String method = callData["method"];
Map<String, dynamic> params = callData["params"];

      if (method == "launchUrl") {
        String url = params["url"];
        await pkg.launchUrl(Uri.parse(url));
      } else if (method == "canLaunchUrl") {
        String url = params["url"];
        bool canLaunch = await pkg.canLaunchUrl(Uri.parse(url));
        sendResponse({"result": canLaunch});
      }
    }
  }
},

);
}
This Dart code:
Listens for call_dart messages from Python.
Parses the method name and parameters.
Calls the corresponding function from the package (e.g., pkg.launchUrl).

Step 6: Generate a Sample Flet App (main.py)
Goal: Create a main.py that demonstrates the package’s functionality.
Process:
Generate main.py with a simple UI that uses the wrapper class.
Example for url_launcher:
python

import flet as ft
from wrappers.url_launcher_wrapper import UrlLauncher

def main(page: ft.Page):
page.title = "URL Launcher Test"
page.vertical_alignment = ft.MainAxisAlignment.CENTER
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER

url_launcher = UrlLauncher()
url_input = ft.TextField(label="Enter URL", value="https://www.google.com")

def on_launch(e):
    url = url_input.value
    url_launcher.launchUrl(url)
    page.add(ft.Text(f"Launched URL: {url}"))

page.add(
    ft.Column(
        [
            ft.Text("URL Launcher Demo", size=20),
            url_input,
            ft.ElevatedButton("Launch URL", on_click=on_launch),
        ],
        alignment=ft.MainAxisAlignment.CENTER,
        horizontal_alignment=ft.CrossAxisAlignment.CENTER,
    )
)

if name == "main":
ft.app(target=main)

Step 7: Ensure Dependencies and Run
Goal: Make sure the app runs without manual setup.
Process:
Ensure flet and flutter are installed and accessible.
The pubspec.yaml already includes the package and flet as dependencies.
Users can run the app with:
bash

flet run main.py

Screenshots

No response

Additional details

import argparse
import os
import subprocess
import re
import json

def parse_dart(package_name):
"""Scan Dart files for public APIs."""
print(f"🔎 Parsing Dart files for '{package_name}'")
global_cache = os.path.expanduser(r"~\AppData\Local\Pub\Cache\hosted\pub.dev")
package_dir = None

if os.path.exists(global_cache):
    for d in os.listdir(global_cache):
        if d.startswith(f"{package_name}-"):
            package_dir = os.path.join(global_cache, d, "lib")
            break

if not package_dir or not os.path.exists(package_dir):
    print(f"❌ Could not find '{package_name}' in pub cache")
    return []

public_functions = set()
for root, _, files in os.walk(package_dir):
    for file in files:
        if file.endswith(".dart"):
            file_path = os.path.join(root, file)
            with open(file_path, "r", encoding="utf-8") as f:
                content = f.read()
                matches = re.findall(r"(?:Future<\w+>|\w+)\s+([a-zA-Z]\w*)\s*\([^)]*\)\s*(?:async\s*)?(?:=>|\{)", content)
                for match in matches:
                    if not match.startswith("_") and match not in ["class", "enum", "typedef", "PlatformException", "Function"]:
                        public_functions.add(match)

public_functions = sorted(public_functions)
print(f"✅ Found public functions: {public_functions}")
return public_functions

def setup_project(package_name):
"""Set up the project structure and dependencies."""
print(f"📁 Setting up project for '{package_name}'")

# Create pubspec.yaml
with open("pubspec.yaml", "w") as f:
    f.write(f'''

name: flet_app
description: A Flet app with integrated Flutter package
environment:
sdk: '>=2.18.0 <4.0.0'
dependencies:
flet: ^0.23.0
{package_name}: any
''')

# Run flutter pub get
print(f"📦 Running 'flutter pub get' for '{package_name}'")
result = subprocess.run(
    [r"C:\src\flutter_windows_3.19.6-stable\flutter\bin\flutter.bat", "pub", "get"],
    capture_output=True,
    text=True
)
if result.returncode != 0:
    print("❌ Error:")
    print(result.stderr)
    return False
print(f"✅ Successfully fetched '{package_name}' from pub.dev")
return True

def generate_wrapper(package_name, functions):
"""Generate Python wrapper for the package."""
print(f"🧠 Generating Python wrapper for: {package_name}")
wrapper_code = f'''

Auto-generated wrapper for {package_name}

import flet as ft
import json

class {package_name.title().replace("_", "")}(ft.Control):
def init(self, **kwargs):
super().init()
self._package = "{package_name}"
for k, v in kwargs.items():
setattr(self, k, v)

def _call_dart(self, method: str, params: dict):
    message = {{
        "method": method,
        "params": params
    }}
    self.page.client_storage.set("dart_call", json.dumps(message))
    self.page.pubsub.send_all("call_dart")

'''
for func in functions:
wrapper_code += f'''
def {func}(self, *args, **kwargs):
params = {{"args": list(args), "kwargs": kwargs}}
self._call_dart("{func}", params)
'''
os.makedirs("wrappers", exist_ok=True)
output_file = f"wrappers/{package_name}_wrapper.py"
with open(output_file, "w") as f:
f.write(wrapper_code)
print(f"✅ Wrapper generated at {output_file}")

def generate_dart_bridge(package_name):
"""Generate the Dart-Python bridge in main.dart."""
print(f"🌉 Generating Dart-Python bridge for '{package_name}'")
dart_code = f'''
import 'dart:convert';
import 'package:flet/flet.dart';
import 'package:flutter/material.dart';
import 'package:{package_name}/{package_name}.dart' as pkg;

void main() async {{
await runAppWithFlet(
(pageClient) async {{
return MaterialApp(
home: FletApp(pageClient: pageClient),
);
}},
onMessage: (message, sendResponse) async {{
if (message["topic"] == "call_dart") {{
String? dartCall = pageClient.clientStorage.get("dart_call");
if (dartCall != null) {{
Map<String, dynamic> callData = jsonDecode(dartCall);
String method = callData["method"];
Map<String, dynamic> params = callData["params"];

      // Handle {package_name} methods
      if (method == "launchUrl") {{
        String url = params["args"][0];
        await pkg.launchUrl(Uri.parse(url));
      }}
    }}
  }}
}},

);
}}
'''
os.makedirs("flutter", exist_ok=True)
with open("flutter/main.dart", "w") as f:
f.write(dart_code)
print("✅ Dart bridge generated at flutter/main.dart")

def generate_main_app(package_name):
"""Generate a sample Flet app in main.py."""
print(f"🎨 Generating sample Flet app for '{package_name}'")
main_code = f'''
import flet as ft
from wrappers.{package_name}wrapper import {package_name.title().replace("", "")}

def main(page: ft.Page):
page.title = "{package_name.title()} Test"
page.vertical_alignment = ft.MainAxisAlignment.CENTER
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER

{package_name} = {package_name.title().replace("_", "")}()
url_input = ft.TextField(label="Enter URL", value="https://www.google.com")

def on_launch(e):
    url = url_input.value
    {package_name}.launchUrl(url)
    page.add(ft.Text(f"Launched URL: {{url}}"))

page.add(
    ft.Column(
        [
            ft.Text("{package_name.title()} Demo", size=20),
            url_input,
            ft.ElevatedButton("Launch URL", on_click=on_launch),
        ],
        alignment=ft.MainAxisAlignment.CENTER,
        horizontal_alignment=ft.CrossAxisAlignment.CENTER,
    )
)

if name == "main":
ft.app(target=main)
'''
with open("main.py", "w") as f:
f.write(main_code)
print("✅ Sample app generated at main.py")

def integrate_package(package_name):
"""Integrate a Flutter package into a Flet project."""
print(f"🚀 Integrating package '{package_name}'")

# Step 1: Set up project and download package
if not setup_project(package_name):
    return

# Step 2: Parse Dart code
functions = parse_dart(package_name)
if not functions:
    print("⚠️ No public functions found. Wrapper will be minimal.")

# Step 3: Generate Python wrapper
generate_wrapper(package_name, functions)

# Step 4: Generate Dart-Python bridge
generate_dart_bridge(package_name)

# Step 5: Generate sample Flet app
generate_main_app(package_name)

print(f"🎉 Integration complete! Run 'flet run main.py' to test the app.")

def main():
parser = argparse.ArgumentParser(description="Flet Flutter Package Integration Tool")
subparsers = parser.add_subparsers(dest="command")
integrate_parser = subparsers.add_parser("integrate", help="Integrate a Flutter package into a Flet project")
integrate_parser.add_argument("package_name")
args = parser.parse_args()

if args.command == "integrate":
    integrate_package(args.package_name)
else:
    parser.print_help()

if name == "main":
main()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions