DEV Community

TheEthicalBoy
TheEthicalBoy

Posted on

Build a URL Shortener Flutter App With Flet Python Framework

Feel free to scroll down to the end of this article to look at the final result. :)

Project’s Dependencies

For this project, we will need just two python packages: Flet and Pyshorteners.

Flet will be the frontend person in charge of the building and rendering of the UI, while Pyshorteners will be the backend person in charge of shortening the given URLs/links.

From this, you can already understand that Flet could be combined with any Python library of your choice (pyshorteners, in our case).

Installing our Dependencies

Please make sure you have Python (preferably v3.7 or above) installed, then run these two commands in your terminal/command line:

pip install flet
pip install pyshorteners
Enter fullscreen mode Exit fullscreen mode

I recommend you do this in a Virtual Machine (VM).

After the installations are done, create a new file and name it as you wish (ex: my-url-shortener) and run this basic “Hello, World!” code using your favorite IDE to ensure you are good to go.

import flet as ft

def main(page: ft.Page):
    page.add(ft.Text(value="Hello, world!"))

ft.app(target=main)
Enter fullscreen mode Exit fullscreen mode

Hello World — Dark Mode
Hello World — Light Mode

Throughout this article, I will be importing Flet with the alias ft, just like Numpy is commonly aliased np or Pandas pd.

The execution of that code block opens a native window containing the rendered output. Because we will deal with links, let’s render the output in our web browser. To do that, modify the last line of code, as shown below:

ft.app(target=main, view=ft.WEB_BROWSER)  # add 'port' parameter to specify the port to be used
Enter fullscreen mode Exit fullscreen mode

Hello World in Browser — Dark Mode
Hello World in Browser — Light Mode

The User Interface (UI)

The UI for this project will be straightforward. We will need a text field to get the user input (URL/link), with a button to validate that input.
This input will be sent to the backend for processing, and the output will be displayed as text on the screen.

We will also have a button to copy the generated shortened links and another to open/view the link in a browser tab. Exciting, right?

Application’s Base

Let’s give our application a title and add an app bar.

import flet as ft

def main(page: ft.Page):
    page.title = "URL Shortener"   # title of application/page
    page.horizontal_alignment = "center"     # center our page's content (remove or change at wish)

    page.appbar = ft.AppBar(
        title=ft.Text("URL Shortener", color=ft.colors.WHITE),    # title of the AppBar, with a white color
        center_title=True,          # we center the title
        bgcolor=ft.colors.BLUE,     # a color for the AppBar's background
    )

    page.add(ft.Text(value="Hello, world!"))    # some placeholder content (nothing will be shown without this line)

ft.app(target=main, view=ft.WEB_BROWSER)  # add 'port' parameter to specify the port to be used
Enter fullscreen mode Exit fullscreen mode

Hello World in the browser with title and AppBar — Dark Mode

Hello World in the browser with title and AppBar — Light Mode

After executing your program, you should see something similar to one of the images above.

‘One of the images’ because Flet uses your system’s theme by default. For consistency, let’s use the light theme. So, let’s explicitly set the theme_mode.

#...
def main(page: ft.Page):
    page.theme_mode = "light"    # there are only 3 possible values: "dark", "light" and "system"
    #...
Enter fullscreen mode Exit fullscreen mode

I omit some code lines (using #…) to save some space.

Getting the User’s Input

I mentioned earlier that we will use a text field for this.

Let’s create one, store it in a variable named text_field, and add it to our page/UI.

  # ...
    page.appbar = ft.AppBar(
        # ...
    )

    text_field = ft.TextField(
        value='https://github.com/ndonkoHenri',  # a test link
        label="Long URL",  # the field's label
        hint_text="type long url here",  # the field's hint-text
        max_length=200,  # the maximum length of inputted links
        keyboard_type="url",  # the field's  keyboard type
        suffix=ft.FilledButton("Shorten!"),  # a button in the field, to shorten the inputted links
    )

    page.add(
        text_field,
        ft.Text("Generated URLs:", weight="bold", size=23)
    )  # add our text field to the page/UI

# ...
Enter fullscreen mode Exit fullscreen mode

After execution of your program now, you should see something similar to this:

Textfield for user input collection

Output Display

Before moving on, let’s import pyshorteners below Flet and initialize it.

import flet as ft
import pyshorteners  # pip install pyshorteners

shortener = pyshorteners.Shortener()    # create an instance (initialization)
# ...
Enter fullscreen mode Exit fullscreen mode

Now, when shortening an entered long URL, we will display a Row containing three main things: a shortened link, a copy button, and a button to open the shortened link in a browser tab.

This Row will be displayed several times, so we need to create an isolated and reusable component. We will create a Python class and place it below the shortener variable.

# ...

class ShortLinkRow(ft.Row):
    # a row containing the shortened url, and two buttons ('copy', and 'open in browser')

    def __init__(self, shortened_link, link_source):
        """
        We create a new class called `ShortenedLinkRow` that inherits from `ft.Row`.
        The constructor takes two arguments/parameters: `shortened_link` and `source`.

        :param shortened_link: the shortened link
        :param link_source: the service hosting the shortened_link
        """
        super().__init__()  # required when overwriting the constructor

        self.tooltip = link_source  # set the tooltip of the row itself to the link provider/source
        self.alignment = "center"   # center the contents of this row

        # the controls/content of our Row
        self.controls = [
            ft.Text(value=shortened_link, size=16, selectable=True, italic=True),
            ft.IconButton(
                icon=ft.icons.COPY, # the icon to be showed
                on_click=lambda e: self.copy(shortened_link),   # when this button is clicked, call the `copy` method, passing the shortened link as parameter
                bgcolor=ft.colors.BLUE_700,
                tooltip="copy"  # to be showed when hovering on this button
            ),
            ft.IconButton(
                icon=ft.icons.OPEN_IN_BROWSER_OUTLINED, # the icon to be showed
                tooltip="open in browser",  # to be showed when hovering on this button
                on_click=lambda e: e.page.launch_url(shortened_link)    # when this button is clicked, open a browser tab with that shortened link
            )
        ]

    def copy(self, value):
        """
        It copies the given value to the clipboard, and opens a Snackbar to inform the user.
        :param value: The value to be copied to the clipboard
        """
        self.page.set_clipboard(value)
        self.page.show_snack_bar(
            ft.SnackBar(
                ft.Text("Link copied to clipboard!"),
                open=True
            )
        )
Enter fullscreen mode Exit fullscreen mode

Having this done, we now need to listen to events in the Textfield so that we can show the outputs at the right moment.

We will listen to the click of the ‘Submit’ button and press the ‘Enter’ keyboard key when the Text field is in focus. When one of these events occurs, Flet will run a particular function named shorten, which we will create in a while.

Make the modifications below to your text_field variable:

text_field = ft.TextField(
        # ...
        suffix=ft.FilledButton("Shorten!", on_click=shorten),   # the function to be called when this button will be clicked
        on_submit=shorten  # the function to be called once the user presses the Enter keyboard key to submit entry
    )
Enter fullscreen mode Exit fullscreen mode

Now let’s create the callback function, and add it to the main function.

# ...(below page.appbar assignment)

def shorten(e):
    """Grabs the URL in the textfield, and displays shortened versions of it."""

    user_link = text_field.value     # retrieve the content of the textfield

    if user_link:  # if the textfield is not empty
        # if the entered text in the textfield is not a valid URl, the program may break, hence the need to catch that
        try:
            page.add(ft.Text(f"Long URL: {user_link}", italic=False, weight='bold'))
            page.add(ShortLinkRow(shortened_link=shortener.tinyurl.short(user_link), link_source="By tinyurl.com"))
            page.add(ShortLinkRow(shortened_link=shortener.chilpit.short(user_link), link_source="By chilp.it"))
            page.add(ShortLinkRow(shortened_link=shortener.clckru.short(user_link), link_source="By clck.ru"))
            page.add(ShortLinkRow(shortened_link=shortener.dagd.short(user_link), link_source="By da.dg"))

        except Exception as exception:  # the error might be that a url shortening service from pyshorteners failed to shorten our url
            print(exception)
            # inform the user that an error has occurred
            page.show_snack_bar(
                ft.SnackBar(
                    ft.Text("Sorry, but an error occurred. Please retry, or refresh the page."),
                    open=True
                )
            )

    else:  # if the textfield is empty (no text)
        # inform the user
        page.show_snack_bar(
            ft.SnackBar(
                ft.Text("Please enter a URL in the field!"),
                open=True
            )
        )
# ...
Enter fullscreen mode Exit fullscreen mode

If you got lost along the line, please check this gist for the complete code. Below is a capture of the final result:
Final

I deployed an online version of this app here for you. Test it out and let me please have your impressions.

Note that you could eventually package this app as a standalone executable file or deploy it on the web. That’s just the cross-platform nature of Flet.

It’s Your Turn

To make this project better or more advanced, try this out:

You can use this as a reference/solution if you get stuck. Please, let me know if you tried it or have any questions. I will be happy to help.

Thanks for reading!

Top comments (0)