fluxtual brings a Flutter-inspired API to Textual, aiming to provide a familiar, expressive, and scalable way to build terminal UIs.
It includes a theme system, ready-to-use widgets, strongly typed enums for styling, and utilities for layout and text rendering.
Warning
This project is freezed/archived due i have no time. Maybe will continue work in summer.
Warning
This package is under active development. Expect frequent updates, breaking changes, and a growing set of widgets, helpers, and theming capabilities.
- 🎨 Theme-aware colors and styles
- 🖋 Flutter-like API
- 📐 Typed enums for alignment, font weight/style, overflow, and decoration
- 🧩 Widgets with consistent styling behavior
If you’ve ever built Flutter apps, you’ll feel right at home with Fluxtual.
It brings familiar patterns like Text, TextStyle, and strongly typed enums into the terminal world, making it easier to build complex, themed UIs without manually managing ANSI codes or low-level styles.
Install directly from the GitHub repository:
pip install git+https://github.com/codeflane/fluxtual.gitsoon: PyPI package
We made full copy of flutter standalone Color class and Colors enum.
from fluxtual.color import Color, Colors
# From hex
c1 = Color.from_hex("#FF0000")
# From ARGB
c2 = Color.from_argb(0xFF, 255, 0, 0)
# From int (Flutter-style)
c3 = Color(0xFFFF0000)
#From RGBO (soon)
# Predefined colors enum
print(Colors.red)
print(Colors.amber)A styled text widget with support for alignment, wrapping, overflow handling, and background colors.
from fluxtual.color import Colors
from fluxtual.enums import TextAlign, TextOverflow
from fluxtual.text import Text, TextStyle
style = TextStyle(color=Colors.amber)
text_widget = Text(
data="Hello Fluxtual!",
style=style,
text_align=TextAlign.center,
soft_wrap=True,
overflow=TextOverflow.fade,
max_lines=1
)Describes how text should be painted, merged, and applied to a widget. Supports merging with inheritance, theme-aware defaults, and conversion to CSS for Textual styles.
from fluxtual.color import Colors
from fluxtual.enums import FontStyle, FontWeight, TextDecoration
style = TextStyle(
color=Colors.red,
background_color=Colors.black,
font_style=FontStyle.italic,
font_weight=FontWeight.w700,
decoration=TextDecoration.underline
)
Text picks up defaults from DefaultTextStyle provided by MaterialApp’s theme.
- If a Text has its own style with inherit=True (default), Fluxtual merges it with the theme’s style.
- If inherit=False, the widget uses only its own TextStyle.
| Enum | Values |
|---|---|
| TextAlign | left, right, center, justify |
| FontWeight | w400, w700, normal, bold |
| FontStyle | normal, italic |
| TextDecoration | none, underline, line_through, combined |
| TextOverflow | clip, ellipsis, fade, visible |
A top-level application widget that wires up theming and establishes a root for your Fluxtual widget tree.
Use it to provide a ThemeData (colors + text styles) and a home widget to render.
from fluxtual.app import MaterialApp
from fluxtual.theme import ThemeData, TextTheme, ColorScheme
from fluxtual.widgets.text import Text, TextStyle
from fluxtual.color import Colors
app = MaterialApp(
theme=ThemeData(
color_scheme=ColorScheme.dark(), # or ColorScheme.light(...)
text_theme=TextTheme(
body=TextStyle(color=Colors.white),
title=TextStyle(color=Colors.amber)
),
),
home=Text("Hello Fluxtual!")
)A layout widget that controls how its child is laid out within a Row or Column.
By default, it takes up a portion of the available space determined by flex, but if fit=FlexFit.loose, it only expands if there's free space.
from fluxtual.flex import Flexible
from fluxtual.enums import FlexFit
from textual.widgets import Static
flexible_widget = Flexible(
Static("Short text (loose) – grows only if there is room."),
flex=2,
fit=FlexFit.loose
)| Enum | Values |
|---|---|
| FlexFit | loose, tight |
A layout widget that forces its child to occupy exactly its allocated flex share in a Row or Column, regardless of the child’s intrinsic size.
from fluxtual.flex import Expanded
from textual.widgets import Static
expanded_widget = Expanded(
Static("Expanded fills exactly its fr share."),
flex=1
)Positions a single child within itself using Flutter-like Alignment.
Optionally sizes itself to a multiple of the child via width_factor / height_factor.
from fluxtual.geometry.alignment import Alignment
from fluxtual.widgets.align import Align
from fluxtual.widgets.text import Text
# Center a text
aligned = Align(
child=Text("Centered"),
alignment=Alignment.center,
)
# Bottom-right, container size collapses to 1.5x child width and 2x child height
compact = Align(
child=Text("Bottom Right"),
alignment=Alignment.bottom_right,
width_factor=1.5,
height_factor=2.0,
)Alignment(x, y) uses normalized coordinates in the range [-1.0, 1.0]:
- x = -1 → left, 0 → center, 1 → right
- y = -1 → top, 0 → center, 1 → bottom
Predefined constants:
| Name | Value |
|---|---|
Alignment.top_left |
(-1.0, -1.0) |
Alignment.top_center |
(0.0, -1.0) |
Alignment.top_right |
(1.0, -1.0) |
Alignment.center_left |
(-1.0, 0.0) |
Alignment.center |
(0.0, 0.0) |
Alignment.center_right |
(1.0, 0.0) |
Alignment.bottom_left |
(-1.0, 1.0) |
Alignment.bottom_center |
(0.0, 1.0) |
Alignment.bottom_right |
(1.0, 1.0) |
You can also create custom alignments:
custom = Align(
child=Text("Custom"),
alignment=Alignment(0.25, -0.75), # slightly right of left, near the top
)- Stretch (default): If width_factor/height_factor are None, Align expands to the available space (behaves like a full-size container) and positions the child within it.
- Shrink (factor mode): If a factor is provided, Align reports its own content size as child_size * factor along that axis, effectively collapsing to a size relative to its child (Flutter-like behavior).
A convenience widget that centers its single child within itself. Functionally equivalent to Align(alignment=Alignment.center) with optional width_factor / height_factor.
from fluxtual.widgets.center import Center
from fluxtual.widgets.text import Text
# Center a text within available space
centered = Center(
child=Text("Hello, Fluxtual!")
)A widget that defers the creation of its child to a builder function. Useful when you need to rebuild UI based on context or want to construct widgets lazily.
from fluxtual.widgets.builder import Builder
from fluxtual.widgets.text import Text
# Simple usage: build a text dynamically
builder = Builder(
builder=lambda context: Text(f"Hello from {context.app.title}")
)A widget that displays its children in a one-dimensional array.
from fluxtual.widgets.layouts import Flex
from fluxtual.widgets.text import Text
from fluxtual.enums import MainAxisAlignment, Axis
flex = Flex(
axis=Axis.horizontal,
main_axis_alignment=MainAxisAlignment.center,
spacing=5,
children=[
Text('Centered text'),
Text('and one more!')
]
)A widget that displays its children in a vertical array.
from fluxtual.widgets.layouts import Column
from fluxtual.widgets.text import Text
from fluxtual.enums import MainAxisAlignment, Axis
column = Column(
main_axis_alignment=MainAxisAlignment.space_between,
children=[
Text('text from left'),
Text('text from center'),
Text('text from right')
]
)A widget that displays its children in a horizontal array.
from fluxtual.widgets.layouts import Row
from fluxtual.widgets.text import Text
from fluxtual.enums import MainAxisAlignment, Axis, CrossAxisAlignment
row = Row(
main_axis_alignment=MainAxisAlignment.space_evenly,
cross_axis_alignment=CrossAxisAlignment.end
spacing=5,
children=[
Text('Supports main and'),
Text('and cross alignments'),
Text('even with spacing')
]
)Soon more widgets! Made 9/~300 Flutter widgets
- 1 widget
- 3 widgets
- 5 widgets
- 10 widgets
- 20 widgets
- Release to PyPI
- 50 widgets
- Add documentation to readthedocs.io
- 100 widgets
- 200 widgets
- Text direction support
- Animations
- SnackBars
- Text.rich/TextSpan/InlineSpan support
- Canvas widget support (CircularProgressIndicator, Divider, etc.)
- Radius support (ClipRRect widget, border_radius argument)
This project licensed under MIT License
This project is still in active development, and your help is highly appreciated!
If you have ideas, find a bug, or want to add a new widget — feel free to:
- Open a Pull Request with your improvements.
- Create an Issue to report problems or suggest features.
- ⭐ Star the repository to support the project and help it grow.