flutter_zoom_image 1.0.0 copy "flutter_zoom_image: ^1.0.0" to clipboard
flutter_zoom_image: ^1.0.0 copied to clipboard

Flutter image zoom package. Provides ZoomImageWidget (standard images) and DeepZoomImageWidget (DZI tile pyramid) with shared gesture + controller layer.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_zoom_image/flutter_zoom_image.dart';


void main() => runApp(const App());

class App extends StatelessWidget {
  const App({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'flutter_zoom_image demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark(useMaterial3: true),
      home: const _Home(),
    );
  }
}

class _Home extends StatefulWidget {
  const _Home();
  @override
  State<_Home> createState() => _HomeState();
}

class _HomeState extends State<_Home> with SingleTickerProviderStateMixin {
  late final TabController _tab;
  @override
  void initState() { super.initState(); _tab = TabController(length: 3, vsync: this); }
  @override
  void dispose() { _tab.dispose(); super.dispose(); }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      appBar: AppBar(
        backgroundColor: Colors.black,
        title: const Text('flutter_zoom_image'),
        bottom: TabBar(
          controller: _tab,
          tabs: const [
            Tab(text: 'Tile Zoom'),
            Tab(text: 'Standard'),
            Tab(text: 'Styled'),
          ],
        ),
      ),
      body: TabBarView(
        controller: _tab,
        children: const [_TileZoomExample(), _StandardExample(), _StyledExample()],
      ),
    );
  }
}

// ── Tab 1: TileZoom ───────────────────────────────────────────────────────────

class _TileZoomExample extends StatefulWidget {
  const _TileZoomExample();
  @override
  State<_TileZoomExample> createState() => _TileZoomExampleState();
}

class _TileZoomExampleState extends State<_TileZoomExample> {
  double _zoom = 4.0;
  double _tileSize = 120.0;
  Offset _tilePos = Offset.zero;
  int _imgIdx = 0;

  static const _images = [
    'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=2000',
    'https://images.unsplash.com/photo-1501854140801-50d01698950b?w=2000',
    'https://images.unsplash.com/photo-1470770841072-f978cf4d019e?w=2000',
  ];

  @override
  Widget build(BuildContext context) {
    return Column(children: [
      Expanded(
        child: TileZoom(
          image: NetworkImage(_images[_imgIdx]),
          zoomLevel: _zoom,
          tileSize: _tileSize,
          fit: BoxFit.contain,
          backgroundColor: Colors.black,
          tileBorderColor: const Color(0xFFEF9F27),
          tileBorderWidth: 2.0,
          tileBorderRadius: 8.0,
          clampTileToImage: true,
          onTilePositionChanged: (o) => setState(() => _tilePos = o),
        ),
      ),
      Container(
        color: Colors.white.withOpacity(0.04),
        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            _Stat('Zoom', '${_zoom.toStringAsFixed(1)}×'),
            _Stat('Tile', '${_tileSize.round()}px'),
            _Stat('X', _tilePos.dx.toStringAsFixed(0)),
            _Stat('Y', _tilePos.dy.toStringAsFixed(0)),
          ],
        ),
      ),
      Padding(
        padding: const EdgeInsets.fromLTRB(16, 8, 16, 0),
        child: Row(children: [
          const Text('Zoom  ', style: TextStyle(fontSize: 12, color: Colors.white54)),
          Expanded(
            child: Slider(
              value: _zoom, min: 1.5, max: 8.0, divisions: 13,
              label: '${_zoom.toStringAsFixed(1)}×',
              onChanged: (v) => setState(() => _zoom = v),
            ),
          ),
        ]),
      ),
      Padding(
        padding: const EdgeInsets.fromLTRB(16, 0, 16, 4),
        child: Row(children: [
          const Text('Tile  ', style: TextStyle(fontSize: 12, color: Colors.white54)),
          Expanded(
            child: Slider(
              value: _tileSize, min: 60, max: 220, divisions: 16,
              label: '${_tileSize.round()}px',
              onChanged: (v) => setState(() => _tileSize = v),
            ),
          ),
        ]),
      ),
      Padding(
        padding: const EdgeInsets.fromLTRB(12, 0, 12, 12),
        child: Row(children: [
          _Btn('Next image', () => setState(() => _imgIdx = (_imgIdx + 1) % _images.length)),
        ]),
      ),
    ]);
  }
}

// ── Tab 2: ZoomImage standard ─────────────────────────────────────────────────

class _StandardExample extends StatefulWidget {
  const _StandardExample();
  @override
  State<_StandardExample> createState() => _StandardExampleState();
}

class _StandardExampleState extends State<_StandardExample> {
  final _ctrl = ZoomImageController(minZoom: 1, maxZoom: 8);
  double _zoom = 1;
  Offset _pan = Offset.zero;

  @override
  void dispose() { _ctrl.dispose(); super.dispose(); }

  @override
  Widget build(BuildContext context) {
    return Column(children: [
      Expanded(
        child: ZoomImage(
          image: const NetworkImage(
            'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=2000',
          ),
          controller: _ctrl,
          minZoom: 1,
          maxZoom: 8,
          doubleTapZoom: 3,
          showControls: true,
          showZoomBadge: true,
          decoration: const ZoomImageDecoration(backgroundColor: Colors.black),
          onZoomChanged: (z) => setState(() => _zoom = z),
          onPanChanged: (o) => setState(() => _pan = o),
        ),
      ),
      Container(
        color: Colors.white.withOpacity(0.04),
        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
        child: Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
          _Stat('Zoom', '${(_zoom * 100).round()}%'),
          _Stat('Pan X', '${_pan.dx.toStringAsFixed(1)}px'),
          _Stat('Pan Y', '${_pan.dy.toStringAsFixed(1)}px'),
        ]),
      ),
      Padding(
        padding: const EdgeInsets.fromLTRB(12, 8, 12, 12),
        child: Row(children: [
          const Text('API:', style: TextStyle(fontSize: 12, color: Colors.white54)),
          const SizedBox(width: 8),
          _Btn('1×', () => _ctrl.zoomTo(1)),
          _Btn('2×', () => _ctrl.zoomTo(2)),
          _Btn('4×', () => _ctrl.zoomTo(4)),
          _Btn('Reset', _ctrl.reset),
        ]),
      ),
    ]);
  }
}

// ── Tab 3: Styled ZoomImage ───────────────────────────────────────────────────

class _StyledExample extends StatelessWidget {
  const _StyledExample();
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(24),
      child: Center(
        child: SizedBox(
          height: 420,
          child: ZoomImage(
            image: const NetworkImage(
              'https://images.unsplash.com/photo-1501854140801-50d01698950b?w=2000',
            ),
            minZoom: 1,
            maxZoom: 6,
            doubleTapZoom: 2,
            decoration: ZoomImageDecoration(
              backgroundColor: Colors.grey.shade900,
              borderRadius: BorderRadius.circular(20),
              border: Border.all(color: Colors.white12),
              boxShadow: const [
                BoxShadow(color: Color(0x66000000), blurRadius: 32, offset: Offset(0, 12)),
              ],
            ),
            showControls: true,
            controlsStyle: const ZoomImageControlsStyle(
              alignment: Alignment.bottomLeft,
              padding: EdgeInsets.all(16),
              buttonSize: 42,
              backgroundColor: Color(0xDD1A1A2E),
              iconColor: Colors.white,
              zoomStep: 0.75,
            ),
            showZoomBadge: true,
          ),
        ),
      ),
    );
  }
}

// ── Shared UI ─────────────────────────────────────────────────────────────────

class _Stat extends StatelessWidget {
  const _Stat(this.label, this.value);
  final String label; final String value;
  @override
  Widget build(BuildContext context) => Column(mainAxisSize: MainAxisSize.min, children: [
    Text(label, style: const TextStyle(fontSize: 10, color: Colors.white54)),
    const SizedBox(height: 2),
    Text(value, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500, color: Colors.white)),
  ]);
}

class _Btn extends StatelessWidget {
  const _Btn(this.label, this.onTap);
  final String label; final VoidCallback onTap;
  @override
  Widget build(BuildContext context) => Padding(
    padding: const EdgeInsets.only(right: 8),
    child: OutlinedButton(
      onPressed: onTap,
      style: OutlinedButton.styleFrom(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
        minimumSize: Size.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap,
        side: const BorderSide(color: Colors.white24),
        foregroundColor: Colors.white70, textStyle: const TextStyle(fontSize: 12),
      ),
      child: Text(label),
    ),
  );
}
2
likes
150
points
43
downloads

Documentation

API reference

Publisher

verified publisherdevxhub.com

Weekly Downloads

Flutter image zoom package. Provides ZoomImageWidget (standard images) and DeepZoomImageWidget (DZI tile pyramid) with shared gesture + controller layer.

Homepage

License

MIT (license)

Dependencies

flutter, http

More

Packages that depend on flutter_zoom_image