├── .gitattributes
├── .gitignore
├── Dockerfile
├── README.md
├── appveyor.yml
├── assets
├── index.html
└── manifest.json
├── fly.toml
├── main.py
├── requirements.txt
├── v1.py
└── v2.py
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__/
2 | .Python
3 | build/
4 | MANIFEST
5 | # PyInstaller
6 | # Usually these files are written by a python script from a template
7 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
8 | *.manifest
9 | *.spec
10 | .cache
11 | .env
12 | .venv
13 | env/
14 | venv/
15 | ENV/
16 | /assets/captures/
17 | /dist/
18 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3-alpine
2 |
3 | WORKDIR /app
4 |
5 | COPY requirements.txt ./
6 | RUN pip install --no-cache-dir -r requirements.txt
7 |
8 | COPY . .
9 |
10 | EXPOSE 8080
11 |
12 | CMD ["python", "./main.py"]
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flet-Color-Browser
2 |
3 | 
4 | 
5 | 
6 | 
7 | 
8 |
9 | > _From my experience, working with colors is not that easy._
10 | ## Table of contents:
11 | - [Introduction](#what-is-this-about-introduction)
12 | - [Source of Inspiration](#source-of-inspiration)
13 | - [Screen captures](#screen-captures)
14 | - [How to get started?](#how-to-get-started)
15 | - [How to deploy?](#how-to-deploy)
16 | - [Issues/Contribution](#issuescontribution)
17 |
18 |
19 | ### What is this about? (Introduction)
20 |
21 | A simple but sophisticated tool(Web and desktop UI) for easy color selection when developing [Flet](https://flet.dev/) applications.
22 | Here is a link to the online/web version of this tool -> [flet-colors-browser.fly.dev](https://flet-colors-browser.fly.dev/)
23 |
24 | ### Source of Inspiration
25 |
26 | I decided to build up this tool after looking at the [Flet-Icons-Browser](https://github.com/flet-dev/examples/tree/main/python/apps/icons-browser) - a simple browser which eases Icon selection when developing Flet apps .
27 | This tool is actually a refactored-clone(or fork if you want) of it.
28 | I just added my personal UI touch and included more comments in the code :)
29 |
30 | ### Screen captures
31 | This tool has two versions: One using a [GridView](https://flet.dev/docs/controls/gridview) to display the colors, and another using a [ListView](https://flet.dev/docs/controls/listview) in [Tabs](https://flet.dev/docs/controls/tabs).
32 |
33 | Below are some OLD captures I made of the tool in execution.
34 |
35 | - _**Version 1:**_
36 | - _Dark Mode_
37 |
38 | 
39 |
40 | - _Light Mode_
41 |
42 | 
43 |
44 |
45 | - _**Version 2:**_
46 | - _Dark Mode_
47 |
48 | 
49 |
50 |
51 | - _Light Mode_
52 |
53 | 
54 |
55 | - **GIF Video**
56 |
57 | 
58 |
59 | ### How to get started?
60 |
61 | **Easiest Way:** You can [open it on your browser](https://flet-colors-browser.fly.dev/) and install it as a PWA(Progressive Web Application). It would then be found on your desktop(Windows, macOS, Linux.. etc), and you could run it any time. Follow the guide below gotten from a [Post on Medium](https://medium.com/@dhormale/install-pwa-on-windows-desktop-via-google-chrome-browser-6907c01eebe4).
62 |
63 | 
64 |
65 | **Second-Easiest Way:**
66 | - Try the app online from here:
67 | - You can just download an archive(for Windows, macOS and Linux only) from the [releases](https://github.com/ndonkoHenri/Flet-Color-Browser/releases) section, extract this and run the standalone executable file(~25Mo) found in it.
68 |
69 | **Hardest Way:**
70 | - Start by cloning and unzipping this repo: [how-to](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository)
71 | - Enter the directory
72 |
73 | cd Flet-Color-Browser
74 | - Install the requirements:
75 | `pip install requirements.txt -r`
76 | - Run the `main.py` file
77 |
78 | python main.py
79 |
80 | ### How to Deploy?
81 | See [this](https://ndonkohenri.medium.com/deploying-a-flet-app-for-free-on-cloudflare-pages-e56ecc6ce450) article to deploy this flet app and all others you have for freeee.
82 |
83 |
84 | ### Issues/Contribution
85 | I tried my best to make this project simple and easy to understand, but if you have problems/issues while using this :(,
86 | then you are free to raise an issue and I will happily respond.
87 |
88 | If you instead want to contribute(new features, bug/typo fixes, etc), just fork this project and make a pull request. :)
89 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | image:
2 | - Visual Studio 2019
3 | - macOS
4 | - Ubuntu
5 |
6 | skip_branch_with_pr: true
7 | stack: python 3.10
8 |
9 | environment:
10 | GITHUB_TOKEN:
11 | secure: iL/1Mx78gXHkbeSqsj1N7TyM4pvz3sihozkTOA1PXmCF1FGBM2EzG1seaZ+ca4yZ
12 |
13 | install:
14 | - pip install -r requirements.txt
15 | - pip install pyinstaller
16 |
17 | build_script:
18 | - flet pack main.py --name Flet-Colors-Browser --product-name Flet-Colors-Browser --product-version "2.0" --copyright "Copyright (c) 2022 TheEthicalBoy Inc."
19 |
20 | test: off
21 |
22 | # Publish artifacts to GitHub Releases on "tag" builds
23 | deploy:
24 | provider: GitHub
25 | auth_token: $(GITHUB_TOKEN)
26 | on:
27 | APPVEYOR_REPO_TAG: true
28 |
29 | #
30 | # Windows package
31 | #
32 | for:
33 | -
34 | matrix:
35 | only:
36 | - image: Visual Studio 2019
37 |
38 | after_build:
39 | - 7z a Flet-Colors-Browser-windows.zip %CD%\dist\*.exe
40 |
41 | artifacts:
42 | - path: Flet-Colors-Browser-windows.zip
43 |
44 | #
45 | # macOS package
46 | #
47 | -
48 | matrix:
49 | only:
50 | - image: macOS
51 |
52 | after_build:
53 | - tar -czvf Flet-Colors-Browser-macos.tar.gz -C dist Flet-Colors-Browser.app
54 |
55 | artifacts:
56 | - path: Flet-Colors-Browser-macos.tar.gz
57 |
58 | #
59 | # Linux package
60 | #
61 | -
62 | matrix:
63 | only:
64 | - image: Ubuntu
65 |
66 | after_build:
67 | - tar -czvf Flet-Colors-Browser-linux.tar.gz -C dist Flet-Colors-Browser
68 |
69 | artifacts:
70 | - path: Flet-Colors-Browser-linux.tar.gz
71 |
--------------------------------------------------------------------------------
/assets/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | Flet Colors Browser
25 |
26 |
27 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
101 |
102 |
103 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/assets/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Flet Colors Browser",
3 | "short_name": "Flet Colors Browser",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#ffffff",
7 | "theme_color": "#0175C2",
8 | "description": "Makes color selection easy and simple.",
9 | "orientation": "natural",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | },
22 | {
23 | "src": "icons/icon-maskable-192.png",
24 | "sizes": "192x192",
25 | "type": "image/png",
26 | "purpose": "maskable"
27 | },
28 | {
29 | "src": "icons/icon-maskable-512.png",
30 | "sizes": "512x512",
31 | "type": "image/png",
32 | "purpose": "maskable"
33 | }
34 | ]
35 | }
--------------------------------------------------------------------------------
/fly.toml:
--------------------------------------------------------------------------------
1 | app = "flet-colors-browser"
2 |
3 | kill_signal = "SIGINT"
4 | kill_timeout = 5
5 | processes = []
6 |
7 | [env]
8 | FLET_SERVER_PORT = "8080"
9 |
10 | [experimental]
11 | allowed_public_ports = []
12 | auto_rollback = true
13 |
14 | [[services]]
15 | http_checks = []
16 | internal_port = 8080
17 | processes = ["app"]
18 | protocol = "tcp"
19 | script_checks = []
20 |
21 | [services.concurrency]
22 | hard_limit = 25
23 | soft_limit = 20
24 | type = "connections"
25 |
26 | [[services.ports]]
27 | force_https = true
28 | handlers = ["http"]
29 | port = 80
30 |
31 | [[services.ports]]
32 | handlers = ["tls", "http"]
33 | port = 443
34 |
35 | [[services.tcp_checks]]
36 | grace_period = "1s"
37 | interval = "15s"
38 | restart_limit = 0
39 | timeout = "2s"
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import flet as ft
2 |
3 | # importing version 1
4 | from v1 import ColorBrowser1
5 | # importing version 2
6 | from v2 import ColorBrowser2
7 |
8 |
9 | def main(page: ft.Page):
10 | """App's entry point."""
11 |
12 | page.title = "Colors Browser"
13 | # page.window_always_on_top = True
14 |
15 | # set the minimum width and height of the window.
16 | page.window_min_width = 245
17 | page.window_min_height = 406
18 |
19 | # Setting the theme of the page to light mode.
20 | page.theme_mode = "light"
21 |
22 | # set the width and height of the window.
23 | page.window_width = 562
24 | page.window_height = 720
25 |
26 | page.splash = ft.ProgressBar(visible=False)
27 |
28 | def change_theme(e):
29 | """It changes the theme of the page from dark to light and vice versa."""
30 | page.theme_mode = "light" if page.theme_mode == "dark" else "dark"
31 | theme_icon_button.selected = not theme_icon_button.selected
32 | page.update()
33 |
34 | def handle_nav_bar_change(e: ft.ControlEvent):
35 | """
36 | The handle_nav_bar_change function is called when the user clicks on a button in the nav bar.
37 | It clears all controls from the page, and then adds only one control to it: The screen that corresponds to
38 | the index of the button clicked.
39 | """
40 | page.controls.clear()
41 | page.add(screens[int(e.data)])
42 |
43 | # button to change theme_mode (from dark to light mode, or the reverse)
44 | theme_icon_button = ft.IconButton(
45 | ft.icons.DARK_MODE,
46 | selected_icon=ft.icons.LIGHT_MODE,
47 | icon_color=ft.colors.BLACK,
48 | icon_size=35,
49 | tooltip="change theme",
50 | on_click=change_theme,
51 | style=ft.ButtonStyle(
52 | color={"": ft.colors.BLACK, "selected": ft.colors.WHITE},
53 | ),
54 | )
55 |
56 | page.appbar = ft.AppBar(
57 | title=ft.Text("Colors Browser", color="white"),
58 | center_title=True, bgcolor="blue",
59 | actions=[theme_icon_button],
60 | )
61 |
62 | page.navigation_bar = ft.NavigationBar(
63 | selected_index=1,
64 | destinations=[
65 | ft.NavigationDestination(
66 | icon=ft.icons.LOOKS_ONE_OUTLINED,
67 | selected_icon=ft.icons.LOOKS_ONE,
68 | label="Version 1"
69 | ),
70 | ft.NavigationDestination(
71 | icon=ft.icons.LOOKS_TWO_OUTLINED,
72 | selected_icon=ft.icons.LOOKS_TWO,
73 | label="Version 2"
74 | ),
75 | ],
76 | on_change=handle_nav_bar_change,
77 | )
78 |
79 | # Creating two versions of the page, one with the colors in a grid, and the other with the colors in a list.
80 | version_1 = ColorBrowser1()
81 | version_2 = ColorBrowser2(page)
82 |
83 | screens = [version_1, version_2]
84 |
85 | page.add(
86 | screens[1]
87 | )
88 |
89 |
90 | ft.app(target=main, view=ft.AppView.WEB_BROWSER)
91 | # OR flet.app(target=main, view=flet.WEB_BROWSER, port=5050) then open http://localhost:5050/
92 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | flet == 0.10.2
2 |
--------------------------------------------------------------------------------
/v1.py:
--------------------------------------------------------------------------------
1 | import os
2 | from itertools import islice
3 | import flet as ft
4 |
5 | # Increasing the maximum message size that can be sent over the websocket.
6 | os.environ["FLET_WS_MAX_MESSAGE_SIZE"] = "6000000"
7 |
8 |
9 | class ColorBrowser1(ft.UserControl):
10 | def __init__(self):
11 | super().__init__()
12 | self.expand = True
13 |
14 | def build(self):
15 | def batches(iterable, batch_size):
16 | """
17 | It takes an iterable and a batch size, and returns an iterator that yields batches of the iterable
18 |
19 | :param iterable: An iterable object (e.g. a list)
20 | :param batch_size: The number of items to process in each batch
21 | """
22 | iterator = iter(iterable)
23 | while batch := list(islice(iterator, batch_size)):
24 | yield batch
25 |
26 | # fetch all icon constants from colors.py module and store them in a dict(colors_dict)
27 | colors_dict = dict()
28 | list_started = False
29 | for key, value in vars(ft.colors).items():
30 | if key == "PRIMARY":
31 | # 'PRIMARY' is the first color-variable (our starting point)
32 | list_started = True
33 | if list_started:
34 | # when this list_started is True, we create new key-value pair in our dictionary
35 | colors_dict[key] = value
36 |
37 | # Creating a text field
38 | search_txt = ft.TextField(
39 | expand=1,
40 | hint_text="Enter keyword and press search button",
41 | autofocus=True,
42 | on_submit=lambda e: display_colors(e.control.value),
43 | tooltip="search field",
44 | label="Color Search Field"
45 | )
46 |
47 | def search_click(e):
48 | """
49 | Called when the search button is pressed, it displays the colors.
50 | """
51 | display_colors(search_txt.value)
52 |
53 | # Creating a row with a search text field and a search button.
54 | search_query = ft.Row(
55 | [search_txt, ft.FloatingActionButton(icon=ft.icons.SEARCH, on_click=search_click, tooltip="search")]
56 | )
57 |
58 | # Creating a grid view with 10 columns and 150 pixels as the maximum extent of each column.
59 | search_results = ft.GridView(
60 | expand=1, runs_count=10, max_extent=150, spacing=5, run_spacing=5, child_aspect_ratio=1,
61 | )
62 | status_bar = ft.Text()
63 |
64 | def copy_to_clipboard(e):
65 | """
66 | When the user clicks on a color, copy the color key to the clipboard
67 |
68 | :param e: The event object
69 | """
70 |
71 | color_key = e.control.data
72 | print("Copied to clipboard:", color_key)
73 | self.page.set_clipboard(e.control.data)
74 | self.page.show_snack_bar(ft.SnackBar(ft.Text(f"Copied {color_key}")))
75 |
76 | def search_colors(search_term: str):
77 | """
78 | It takes a search term as an argument, and then loops through the colors_dict dictionary,
79 | checking if the search term is in the color name or the color value. If it is, it yields the color key
80 |
81 | :param search_term: The search term that the user entered
82 | :return color_key: str
83 | """
84 |
85 | for color_key, color_value in colors_dict.items():
86 | # the color_key has underscores while the color_value doesn't. We take this into consideration
87 | if search_term and (search_term in color_value or search_term in color_key.lower()):
88 | yield color_key
89 |
90 | def display_colors(search_term: str):
91 | """
92 | It takes a search term, disables the search box, cleans the search results(grid view),
93 | and then loops through the search results in batches of 40, adding each color to the search results
94 |
95 | :param search_term: str
96 | """
97 |
98 | # disable the text field and the search button, and clean search results
99 | search_query.disabled = True
100 | self.update()
101 | search_results.clean()
102 |
103 | # Adding the colors to the grid view in batches of 40.
104 | for batch in batches(search_colors(search_term.lower()), 40):
105 | for color_key in batch:
106 | flet_color_key = f"colors.{color_key}"
107 |
108 | search_results.controls.append(
109 | ft.TextButton(
110 | content=ft.Container(
111 | content=ft.Column(
112 | [
113 | ft.Icon(name=ft.icons.RECTANGLE, size=38, color=colors_dict[color_key], ),
114 | ft.Text(
115 | value=f"{colors_dict[color_key]}", size=14, width=100,
116 | no_wrap=True, text_align=ft.TextAlign.CENTER, color=colors_dict[color_key],
117 | ),
118 | ],
119 | spacing=5,
120 | alignment=ft.MainAxisAlignment.CENTER,
121 | horizontal_alignment=ft.CrossAxisAlignment.CENTER,
122 | ),
123 | alignment=ft.alignment.center,
124 | ),
125 | tooltip=f"{flet_color_key}\nClick to copy to a clipboard",
126 | on_click=copy_to_clipboard,
127 | data=flet_color_key,
128 | )
129 | )
130 | status_bar.value = f"Colors found: {len(search_results.controls)}"
131 | self.update()
132 |
133 | # It checks if the search results are empty, and if they are, it shows a snack bar some message
134 | if len(search_results.controls) == 0:
135 | # if no color was found containing the user's search term
136 | self.page.show_snack_bar(ft.SnackBar(ft.Text("No colors found")))
137 | search_query.disabled = False
138 | self.update()
139 |
140 | return ft.Column(
141 | [
142 | search_query,
143 | search_results,
144 | status_bar,
145 | ],
146 | expand=True,
147 | )
148 |
149 |
150 | # (running the app)
151 | if __name__ == "__main__":
152 | def main(page: ft.Page):
153 | page.title = "Flet Colors Browser V1"
154 | # page.window_always_on_top = True
155 |
156 | # set the width and height of the window.
157 | page.window_width = 562
158 | page.window_height = 720
159 |
160 | # Setting the theme of the page to light mode.
161 | page.theme_mode = "light"
162 |
163 | # set the minimum width and height of the window.
164 | page.window_min_width = 245
165 | page.window_min_height = 406
166 |
167 | def change_theme(e):
168 | page.theme_mode = "light" if page.theme_mode == "dark" else "dark"
169 | theme_icon_button.selected = not theme_icon_button.selected
170 | page.update()
171 |
172 | # button to change theme_mode (from dark to light mode, or the reverse)
173 | theme_icon_button = ft.IconButton(
174 | ft.icons.DARK_MODE,
175 | selected_icon=ft.icons.LIGHT_MODE,
176 | icon_color=ft.colors.BLACK,
177 | icon_size=35,
178 | tooltip="change theme",
179 | on_click=change_theme,
180 | style=ft.ButtonStyle(
181 | color={"": ft.colors.BLACK, "selected": ft.colors.WHITE},
182 | ),
183 | )
184 |
185 | # Creating an AppBar object and assigning it to the page.appbar attribute.
186 | page.appbar = ft.AppBar(
187 | title=ft.Text("Colors Browser V1", color="white"),
188 | center_title=True,
189 | bgcolor="blue",
190 | actions=[theme_icon_button],
191 | )
192 |
193 | # Creating an instance of the ColorBrowser1 class.
194 | version_1 = ColorBrowser1()
195 |
196 | # adds the color browser to the page
197 | page.add(
198 | version_1
199 | )
200 |
201 |
202 | ft.app(target=main)
203 | # OR flet.app(target=main, view=flet.WEB_BROWSER, port=5050) then open http://localhost:5050/
204 |
--------------------------------------------------------------------------------
/v2.py:
--------------------------------------------------------------------------------
1 | import flet as ft
2 |
3 |
4 | # It's a container(or list-item) that has a descriptive text in it, and when clicked,
5 | # it copies the text to the clipboard
6 | class Tile(ft.Container):
7 | def __init__(self, tile_text, color, page):
8 | super().__init__()
9 | self.text = ft.Text(tile_text, text_align=ft.TextAlign.CENTER, weight=ft.FontWeight.BOLD, italic=True, )
10 | self.color_text = f"colors.{tile_text}"
11 | self.bgcolor = color
12 | self.expand = True
13 | self.height = 40
14 | self.content = self.text
15 | self.page = page
16 | self.tooltip = "Click to copy to Clipboard"
17 |
18 | def _build(self):
19 | def click_event(e):
20 | """
21 | It copies the color's text to the clipboard.
22 |
23 | :param e: The event that triggered the function
24 | """
25 | print("Copied to clipboard:", self.color_text)
26 | self.page.set_clipboard(self.color_text)
27 | self.page.show_snack_bar(ft.SnackBar(ft.Text(f"Copied {self.color_text}!")))
28 |
29 | self.on_click = click_event
30 |
31 | return self
32 |
33 |
34 | class ColorBrowser2(ft.UserControl):
35 | def __init__(self, page):
36 | super().__init__()
37 | self.expand = True
38 |
39 | # A reference to the page object that is passed to the constructor of the class.
40 | self.page = page
41 |
42 | # Creating a reference to the Tabs object that will be created later.
43 | self.displayed_tabs = ft.Ref[ft.Tabs]()
44 |
45 | # A list of colors that will be used to create the tabs.
46 | self.original_tab_names = [
47 | 'RED', "BLACK", "WHITE", 'PINK', 'PURPLE', 'DEEP_PURPLE', 'INDIGO', 'BLUE', 'LIGHT_BLUE', "GREY",
48 | 'CYAN', 'TEAL', 'GREEN', 'LIGHT_GREEN', 'LIME', 'YELLOW', 'AMBER', "ORANGE", 'DEEP_ORANGE',
49 | 'BROWN', 'BLUE_GREY'
50 | ]
51 |
52 | def build(self):
53 |
54 | # Getting all the colors from the colors module.
55 | list_started = False
56 | all_flet_colors = list()
57 | for key in vars(ft.colors).keys():
58 | if key == "PRIMARY":
59 | list_started = True
60 | if list_started:
61 | all_flet_colors.append(key)
62 |
63 | def create_tabs(tab_names: list) -> list:
64 | """
65 | It takes a list of strings where each string represents the name of a tab to be shown, and returns a list
66 | of tabs, each tab containing a list of tiles(containers) having a specific background color associated with the text in it.
67 |
68 | :param tab_names: list of strings that will be used to create the tabs
69 | :type tab_names: list
70 | :return: A list of tabs
71 | """
72 | created_tabs = []
73 | found = []
74 | # iterate over the tab_names(list containing the tabs to be shown)
75 | for tab_name in tab_names:
76 | tab_content = ft.ListView()
77 | for color in all_flet_colors:
78 | tile_bgcolor = color.lower().replace("_", "")
79 | tile_content = Tile(color, tile_bgcolor, self.page)
80 | # Checking if the color starts with the tab_name and if the tab_name is in the color.
81 | if (tab_name in color) and color.startswith(tab_name):
82 | tab_content.controls.append(tile_content)
83 | found.append(color)
84 |
85 | # Add a tab with the name of the color and the content of the tab is a list of tiles.
86 | # Also remove underscores from the tab's name.
87 | created_tabs.append(ft.Tab(tab_name.replace("_", " "), content=tab_content, ))
88 |
89 | # Creating a tab called "OTHERS" and adding all the colors that were not added to any other tab to it.
90 | others = [i for i in all_flet_colors if i not in found]
91 | others_content = ft.ListView(
92 | controls=[Tile(x, x.lower().replace("_", ""), self.page) for x in others]
93 | )
94 | created_tabs.append(ft.Tab("OTHERS", content=others_content))
95 |
96 | return created_tabs
97 |
98 | # self.displayed_tabs.current.tabs = create_tabs(self.original_tab_names)
99 |
100 | def filter_tabs(e: ft.ControlEvent):
101 | """
102 | If the text in the search field is "ALL", show all tabs. If the search field is not empty, show only the
103 | tabs that contain the search term.
104 |
105 | :param e: ControlEvent
106 | :type e: ControlEvent
107 | """
108 | # Making the progress bar visible.
109 | self.page.splash.visible = True
110 | self.page.update()
111 | filtered_tab_names = []
112 |
113 | if search_field.value and search_field.value.lower().strip() == "all":
114 | filtered_tab_names = self.original_tab_names
115 | else:
116 | for tab_name in self.original_tab_names:
117 | if search_field.value and search_field.value.lower().strip() in tab_name.lower().replace("_", " "):
118 | filtered_tab_names.append(tab_name)
119 |
120 | if filtered_tab_names:
121 | # Removing all the tabs from the Tabs object.
122 | self.displayed_tabs.current.clean()
123 | # self.page.update()
124 |
125 | # Updating the tabs of the Tabs object.
126 | self.displayed_tabs.current.tabs = create_tabs(filtered_tab_names)
127 | self.displayed_tabs.current.update()
128 | return
129 |
130 | self.page.update()
131 |
132 | # creating a field which will t=help the user search for specific tabs
133 | search_field = ft.TextField(
134 | label="Search Tabs...",
135 | prefix_icon=ft.icons.SEARCH,
136 | on_submit=filter_tabs,
137 | border_radius=50,
138 | suffix=ft.IconButton(
139 | icon=ft.icons.CHECK,
140 | bgcolor=ft.colors.INVERSE_PRIMARY,
141 | icon_color=ft.colors.ERROR,
142 | on_click=filter_tabs
143 | ),
144 | helper_text="Tip: Enter 'ALL' to show all the tabs",
145 | height=70, width=450,
146 | keyboard_type=ft.KeyboardType.TEXT,
147 | capitalization=ft.TextCapitalization.CHARACTERS,
148 | )
149 |
150 | return ft.Column(
151 | controls=[
152 | search_field,
153 | ft.Tabs(
154 | ref=self.displayed_tabs,
155 | expand=True,
156 | tabs=create_tabs(self.original_tab_names)
157 | )
158 | ]
159 | )
160 |
161 |
162 | # (running the app)
163 | if __name__ == "__main__":
164 | def main(page: ft.Page):
165 | page.title = "Flet Colors Browser V2"
166 | # page.window_always_on_top = True
167 |
168 | # set the width and height of the window.
169 | page.window_width = 562
170 | page.window_height = 720
171 |
172 | # Setting the theme of the page to light mode.
173 | page.theme_mode = "light"
174 |
175 | # set the minimum width and height of the window.
176 | page.window_min_width = 245
177 | page.window_min_height = 406
178 |
179 | def change_theme(e):
180 | page.theme_mode = "light" if page.theme_mode == "dark" else "dark"
181 | theme_icon_button.selected = not theme_icon_button.selected
182 | page.update()
183 |
184 | # button to change theme_mode (from dark to light mode, or the reverse)
185 | theme_icon_button = ft.IconButton(
186 | ft.icons.DARK_MODE, selected_icon=ft.icons.LIGHT_MODE,
187 | icon_color=ft.colors.BLACK,
188 | icon_size=35,
189 | tooltip="change theme",
190 | on_click=change_theme,
191 | style=ft.ButtonStyle(color={"": ft.colors.BLACK, "selected": ft.colors.WHITE}),
192 | )
193 |
194 | # Creating an AppBar object and assigning it to the page.appbar attribute.
195 | page.appbar = ft.AppBar(
196 | title=ft.Text("Colors Browser V2", color="white"),
197 | center_title=True,
198 | bgcolor="blue",
199 | actions=[theme_icon_button],
200 | )
201 |
202 | # Creating an instance of the ColorBrowser2 class and passing the page object to it.
203 | version_2 = ColorBrowser2(page)
204 |
205 | # adds the color browser to the page
206 | page.add(
207 | version_2
208 | )
209 |
210 |
211 | ft.app(target=main)
212 | # OR flet.app(target=main, view=flet.WEB_BROWSER, port=5050) then open http://localhost:5050/
213 |
--------------------------------------------------------------------------------