InteractiveViewer
Allows you to pan, zoom, and rotate its content.
Inherits: LayoutControl
Properties
alignment- The origin of the transformation applied to thecontentwhen it is zoomed or panned, relative to the content's size.boundary_margin- A margin for the visible boundaries of thecontent.clip_behavior- Defines how to clip thecontent.constrained- Whether the normal size constraints at this point in the control tree are applied to thecontent.content- TheControlto be transformed.interaction_end_friction_coefficient- Changes the deceleration behavior after a gesture.interaction_update_interval- The minimum interval in milliseconds between intermediateon_interaction_updateandon_transform_changedevents.max_scale- The maximum allowed scale.min_scale- The minimum allowed scale.pan_enabled- Whether panning is enabled.scale_enabled- Whether scaling is enabled.scale_factor- The amount of scale to be performed per pointer scroll.trackpad_scroll_causes_scale- Whether scrolling up/down on a trackpad should cause scaling instead of panning.
Events
on_interaction_end- Called when the user ends a pan or scale gesture.on_interaction_start- Called when the user begins a pan or scale gesture.on_interaction_update- Called when the user updates a pan or scale gesture.on_transform_changed- Called when the effective transform changes.
Methods
get_scale- Return the effective scale currently applied tocontent.get_transform- Return the effective transform currently applied tocontent.pan- Translates the current transform matrix.reset- Resets the current transform matrix to identity.restore_state- Restores the transform matrix previously captured bysave_state.save_state- Saves a snapshot of the current transform matrix.zoom- Applies multiplicative zoom to the current transform.
Examples
Handling events
import flet as ft
def main(page: ft.Page):
page.add(
ft.SafeArea(
content=ft.InteractiveViewer(
min_scale=0.1,
max_scale=15,
boundary_margin=ft.Margin.all(20),
on_interaction_start=lambda e: print(e),
on_interaction_end=lambda e: print(e),
on_interaction_update=lambda e: print(e),
content=ft.Image(src="https://picsum.photos/500/500"),
),
)
)
if __name__ == "__main__":
ft.run(main)
Programmatic transformations
import flet as ft
def main(page: ft.Page):
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
page.vertical_alignment = ft.MainAxisAlignment.CENTER
async def handle_zoom_in(e: ft.Event[ft.Button]):
await i.zoom(1.2)
async def handle_zoom_out(e: ft.Event[ft.Button]):
await i.zoom(0.8)
async def handle_pan(e: ft.Event[ft.Button]):
await i.pan(dx=50, dy=50)
async def handle_reset(e: ft.Event[ft.Button]):
await i.reset()
async def handle_reset_slow(e: ft.Event[ft.Button]):
await i.reset(animation_duration=ft.Duration(seconds=2))
async def handle_save_state(e: ft.Event[ft.Button]):
await i.save_state()
async def handle_restore_state(e: ft.Event[ft.Button]):
await i.restore_state()
async def handle_read_transform(e: ft.Event[ft.Button]):
scale = await i.get_scale()
transform = await i.get_transform()
scale_text.value = (
f"get_scale={scale:.2f} "
f"tx={transform.translation_x:.1f} ty={transform.translation_y:.1f}"
)
scale_text.update()
def handle_transform_changed(e: ft.InteractiveViewerTransformEvent):
scale_text.value = (
f"on_transform_changed scale={e.scale:.2f} "
f"tx={e.translation_x:.1f} ty={e.translation_y:.1f}"
)
scale_text.update()
scale_text = ft.Text("scale=1.00")
page.add(
ft.SafeArea(
content=ft.Column(
controls=[
i := ft.InteractiveViewer(
min_scale=0.1,
max_scale=5,
boundary_margin=ft.Margin.all(20),
on_transform_changed=handle_transform_changed,
content=ft.Image(src="https://picsum.photos/500/500"),
),
scale_text,
ft.Row(
wrap=True,
controls=[
ft.Button("Zoom In", on_click=handle_zoom_in),
ft.Button("Zoom Out", on_click=handle_zoom_out),
ft.Button("Pan", on_click=handle_pan),
ft.Button("Save State", on_click=handle_save_state),
ft.Button("Restore State", on_click=handle_restore_state),
ft.Button("Reset (instant)", on_click=handle_reset),
ft.Button("Reset (slow)", on_click=handle_reset_slow),
ft.Button("Read transform", on_click=handle_read_transform),
],
),
],
),
)
)
if __name__ == "__main__":
ft.run(main)
Properties
alignmentclass-attributeinstance-attribute
alignment: Alignment | None = NoneThe origin of the transformation applied to the content when it is
zoomed or panned, relative to the content's size.
zoom scales the content around this point. It does not change where the
content is placed in this viewer; to position the content, wrap it in a control
that aligns its child, such as a Container with
alignment set.
Gestures and the limits of pan treat the top-left corner as the
origin. With any other value, the content drifts away from the pointer while
zooming with a gesture, and once zoomed in, panning stops short of one edge
of the content and goes past the opposite one, leaving empty space.
boundary_marginclass-attributeinstance-attribute
boundary_margin: MarginValue = field(
default_factory=lambda: Margin.all(0)
)A margin for the visible boundaries of the content.
Any transformation that results in the viewport being able to view outside of the boundaries will be stopped at the boundary. The boundaries do not rotate with the rest of the scene, so they are always aligned with the viewport.
To produce no boundaries at all, pass an infinite value.
Defaults to Margin.all(0), which results in boundaries that are the
exact same size and position as the content.
clip_behaviorclass-attributeinstance-attribute
clip_behavior: ClipBehavior = ClipBehavior.HARD_EDGEDefines how to clip the content.
If set to ClipBehavior.NONE, the content can visually overflow
the bounds of this InteractiveViewer, but gesture events (such as pan or zoom)
will only be recognized within the viewer's area. Ensure this InteractiveViewer
is sized appropriately when using ClipBehavior.NONE.
constrainedclass-attributeinstance-attribute
constrained: bool = TrueWhether the normal size constraints at this point in the control tree are applied to the content.
If set to False, then the content will be given infinite constraints. This
is often useful when a content should be bigger than this InteractiveViewer.
For example, for a content which is bigger than the viewport but can be
panned to reveal parts that were initially offscreen, constrained must
be set to False to allow it to size itself properly. If constrained is
True and the content can only size itself to the viewport, then areas
initially outside of the viewport will not be able to receive user
interaction events. If experiencing regions of the content that are not
receptive to user gestures, make sure constrained is False and the content
is sized properly.
contentinstance-attribute
content: ControlThe Control to be transformed.
Raises:
- ValueError - If it is not visible.
interaction_end_friction_coefficientclass-attributeinstance-attribute
interaction_end_friction_coefficient: Number = 1.35e-05Changes the deceleration behavior after a gesture.
Raises:
- ValueError - If it is not strictly greater than
0.
interaction_update_intervalclass-attributeinstance-attribute
interaction_update_interval: int = 200The minimum interval in milliseconds between intermediate
on_interaction_update and on_transform_changed events.
For on_transform_changed, the final effective transform
is also delivered after changes stop.
max_scaleclass-attributeinstance-attribute
max_scale: Number = 2.5The maximum allowed scale.
Raises:
- ValueError - If it is not strictly greater than
0. - ValueError - If it is not greater than or equal to
min_scale.
min_scaleclass-attributeinstance-attribute
min_scale: Number = 0.8The minimum allowed scale.
The effective scale is limited by the value of boundary_margin.
If scaling would cause the content to be displayed outside the defined boundary,
it is prevented. By default, boundary_margin is set to Margin.all(0),
so scaling below 1.0 is typically not possible unless you increase the
boundary_margin value.
Raises:
- ValueError - If it is not strictly greater than
0. - ValueError - If it is not less than or equal to
max_scale.
scale_enabledclass-attributeinstance-attribute
scale_enabled: bool = TrueWhether scaling is enabled.
scale_factorclass-attributeinstance-attribute
scale_factor: Number = 200The amount of scale to be performed per pointer scroll.
Increasing this value above the default causes scaling to feel slower, while decreasing it causes scaling to feel faster.
Has effect only on pointer device scrolling, not pinch to zoom.
trackpad_scroll_causes_scaleclass-attributeinstance-attribute
trackpad_scroll_causes_scale: bool = FalseWhether scrolling up/down on a trackpad should cause scaling instead of panning.
Events
on_interaction_endclass-attributeinstance-attribute
on_interaction_end: (
EventHandler[ScaleEndEvent[InteractiveViewer]] | None
) = NoneCalled when the user ends a pan or scale gesture.
on_interaction_startclass-attributeinstance-attribute
on_interaction_start: (
EventHandler[ScaleStartEvent[InteractiveViewer]] | None
) = NoneCalled when the user begins a pan or scale gesture.
on_interaction_updateclass-attributeinstance-attribute
on_interaction_update: (
EventHandler[ScaleUpdateEvent[InteractiveViewer]] | None
) = NoneCalled when the user updates a pan or scale gesture.
on_transform_changedclass-attributeinstance-attribute
on_transform_changed: (
EventHandler[
InteractiveViewerTransformEvent[InteractiveViewer]
]
| None
) = NoneCalled when the effective transform changes.
Fires for pinch and pan gestures, pointer-wheel or trackpad zoom, and for
zoom, pan, reset and restore_state.
save_state only stores a snapshot, so it does not fire this event.
Intermediate updates are limited to interaction_update_interval
milliseconds. When changes stop, including the end of a wheel burst, an
inertial pan, zoom and an animated reset, one further
event is delivered if needed. That event matches get_transform.
on_interaction_update reports the gesture's relative scale, not this
absolute transform. Neither event is a physical length: the content's
laid-out size and any mapping from that content to the real world are
separate. If those change, this transform alone does not keep a
real-world measurement correct.
Methods
get_scaleasync
get_scale() -> floatReturn the effective scale currently applied to content.
Returns:
- float -
1.0for identity, after min/max scale and boundary clamping. - float - This is the scale of the laid-out content, not a ratio against
- float - the content's original pixel size, and not a physical length.
Raises:
- RuntimeError - If this control is not on a page.
get_transformasync
get_transform() -> InteractiveViewerTransformReturn the effective transform currently applied to content.
Returns:
- InteractiveViewerTransform - Scale, translation and the column-major 4x4 matrix, after
- InteractiveViewerTransform - clamping. A physical length also needs the content's laid-out
- InteractiveViewerTransform - size and a mapping from that content to the real world.
Raises:
- RuntimeError - If this control is not on a page.
panasync
Translates the current transform matrix.
Parameters:
XY translation is clamped to the same interaction boundaries used
for gesture-driven panning. dz is applied directly to the matrix
and defaults to 0.
resetasync
reset(animation_duration: DurationValue | None = None)Resets the current transform matrix to identity.
Parameters:
- animation_duration (DurationValue | None, default:
None) - Optional animation duration for the reset transition. IfNone, the reset is applied immediately.
When animation_duration is provided, Flutter interpolates from
the current transform to identity using a matrix tween.
restore_stateasync
restore_state()Restores the transform matrix previously captured by
save_state.
If no state has been saved yet, this method has no effect.
save_stateasync
save_state()Saves a snapshot of the current transform matrix.
The saved state can later be restored using
restore_state. Calling this method again
overwrites the previously saved snapshot.
zoomasync
zoom(factor: Number)Applies multiplicative zoom to the current transform.
Parameters:
- factor (Number) - Scale multiplier relative to the current scale. Values greater than
1zoom in, values between0and1zoom out.
The resulting scale is clamped to min_scale and
max_scale. Additional boundary clamping is applied so the
visible viewport remains within boundary_margin limits.