חמש דקות עם flet

17/09/2024

עם כל האהבה לריאקט אני חייב להודות שלא הצלחתי ליהנות מכתיבת קוד React Native. תמיד היה יותר מדי Boilerplate ודברים לא עבדו מספיק כמו שרציתי או שלא נראו מספיק טוב על מכשירים שונים. אני יודע שיש המון אפליקציות טובות שכתובות בריאקט נייטיב ובטוח שהבעיה זה קודם כל אני אבל זה מה שיש ואני חושב שאני לא היחיד בסיפור הזה.

מצד שני הרבה זמן גם התרחקתי מפלאטר בעיקר כי לא היה לי כח ל dart. לכן שמחתי למצוא לאחרונה את flet, סביבה לפיתוח יישומי flutter ב Python. הקוד יצא הרבה יותר נקי וממוקד בהשוואה למה שזכרתי את dart וכמעט לא היו התקנות. ככה זה עובד-

1. התקנה ותוכנית ראשונה

אני מפעיל PyCharm נכנס לתפריט החבילות ומתקין את החבילה flet. עכשיו יוצר קובץ חדש ומדביק את התוכן הבא (רובו מהתיעוד באתר של פלט עם תיבת טקסט שאני הוספתי):

import flet as ft


def main(page: ft.Page):
    page.title = "Flet counter example"
    page.vertical_alignment = ft.MainAxisAlignment.CENTER

    txt_number = ft.TextField(value="0",
                              text_align=ft.TextAlign.RIGHT,
                              width=100)

    def minus_click(e):
        txt_number.value = str(int(txt_number.value) - 1)
        page.update()

    def plus_click(e):
        txt_number.value = str(int(txt_number.value) + 1)
        page.update()

    page.add(
        ft.Row(
            [ft.Text("Counter Example", size=18)],
            alignment=ft.MainAxisAlignment.CENTER,
        ),
        ft.Row(
            [
                ft.IconButton(ft.icons.REMOVE, on_click=minus_click),
                txt_number,
                ft.IconButton(ft.icons.ADD, on_click=plus_click),
            ],
            alignment=ft.MainAxisAlignment.CENTER,
        )
    )

ft.app(main)

קראתי לקובץ demo1.py ונכנסתי למסוף בתוך ה pycharm. בתוך המסוף כתבתי:

flet run -r demo1.py

זה הספיק ל flet בשביל להציג ממשק גרפי על המסך שכלל תיבה עם מספר ושני כפתורים סביבה, אחד לפלוס ושני למינוס. לחיצות על הכפתורים כמובן שינו את המספר. כששיניתי את הקוד באופן אוטומטי הממשק על המסך התרענן כדי לשקף את הקוד החדש. אמריקה.

אחרי זה יצאתי מהתוכנית וכתבתי במסוף:

flet run --web -r demo1.py

וזה הספיק בשביל ש flet יפתח שרת פיתוח, יריץ דפדפן שיתחבר אליו ויציג לי בדיוק את אותו ממשק בתוך הדפדפן. כמו שאמרתי - אמריקה.

2. מאחורי הקלעים

צריך להגיד, החבר'ה ב flet קצת מרמים. הם לא מתרגמים את הפייתון שלנו לפלאטר אלא משתמשים בפלאטר כדי להציג ממשק משתמש שבגדול נוצר דרך הפייתון שלנו. במלים אחרות:

  1. הארכיטקטורה היא ממשק משתמש מרונדר בשרת.

  2. יש שרת שזה יישום שכתוב ב go והוא מקבל JSON-ים שמתארים את ממשק המשתמש.

  3. יש לקוח שזה אפליקציית פלאטר שמחוברת לשרת וקוראת ממנו איזה ממשק להציג.

  4. שרת ה go מריץ את קוד הפייתון, הפייתון מעביר את ה JSON-ים ל go, ה go מייצר הגדרות פלאטר ושולח אותן לאפליקציית ה client. בצורה כזאת בפייתון אנחנו עובדים עם רכיבי UI של flet ולא רכיבים רגילים של פלאטר. כן פלאטר שם אבל הוא מוחבא טוב.

מצאתי לא מעט התלהבות ברשת מ flet וגם מהמעט ששיחקתי איתו הכל עבד חלק ומהר, אבל אי אפשר להתבסס על "פלאטר" ולחשוב שאם אפשר לבנות משהו בפלאטר אז בטוח אפשר יהיה לבנות את זה ב flet. המטרה של flet היא בנייה מהירה של ממשקים לאפליקציה שכבר יש לכם, תוך שימוש בכלים ובסביבת העבודה הרגילה שלכם.

3. השוואה ל dart ופלאטר אמיתי

אני מודה שזכרתי ש Dart היתה שפה מאוד מסורבלת אז בשביל המשחק הלכתי ל Claude וביקשתי שיתרגם לי את הקוד ל dart. זה מה שקיבלתי:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Counter Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _decrementCounter() {
    setState(() {
      _counter--;
    });
  }

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Counter Example'),
      ),
      body: Center(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            IconButton(
              icon: const Icon(Icons.remove),
              onPressed: _decrementCounter,
            ),
            SizedBox(
              width: 100,
              child: TextField(
                controller: TextEditingController(text: _counter.toString()),
                textAlign: TextAlign.right,
                readOnly: true,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                ),
              ),
            ),
            IconButton(
              icon: const Icon(Icons.add),
              onPressed: _incrementCounter,
            ),
          ],
        ),
      ),
    );
  }
}

אני לא יודע אם זה קלוד או שככה dart, אבל גירסת הפייתון נראית הרבה יותר טוב.