├── .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 | ![AppVeyor](https://img.shields.io/appveyor/build/ndonkoHenri/flet-color-browser) 4 | ![Platform](https://img.shields.io/badge/platform-browser%20%7C%20PWA%20%7C%20windows%20%7C%20macos%20%7C%20linux-lightgrey) 5 | ![GitHub top language](https://img.shields.io/github/languages/top/ndonkoHenri/Flet-Color-Browser) 6 | ![GitHub language count](https://img.shields.io/github/languages/count/ndonkoHenri/Flet-Color-Browser) 7 | ![GitHub repo file count](https://img.shields.io/github/directory-file-count/ndonkoHenri/Flet-Color-Browser?color=ry) 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 | ![V1 Dark Mode](https://user-images.githubusercontent.com/98978078/193250212-a56477ac-f063-4b45-99e3-7dbb971eee27.JPG) 39 |

40 | - _Light Mode_ 41 |

42 | ![V1 Light Mode](https://user-images.githubusercontent.com/98978078/193256037-2ee24033-425f-4c9e-958a-f20e3c207917.JPG) 43 | 44 | 45 | - _**Version 2:**_ 46 | - _Dark Mode_ 47 |

48 | ![V2 Light Mode](https://user-images.githubusercontent.com/98978078/193249865-b626c155-7dc3-4188-9fc7-e7a163357c5d.JPG) 49 | 50 |

51 | - _Light Mode_ 52 |

53 | ![Capture](https://user-images.githubusercontent.com/98978078/193256644-3cf3dec8-ca52-4a6e-a772-f567074891f5.JPG) 54 |

55 | - **GIF Video** 56 |

57 | ![Colors-Browser GIF](https://user-images.githubusercontent.com/98978078/193254404-bef6c113-b71d-4e01-b732-cd5d268619ec.gif) 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 | ![How to Install as PWA](https://miro.medium.com/max/720/1*BQ5FlcpuLTOBfF5vLvv6Bg.gif) 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 | Loading... 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 | --------------------------------------------------------------------------------