How to achieve scrollable canvas in Flutter?

3,737

The below Code may help to resolve your problem it scroll the custom canvas in horizontal direction as you have shown in example image.

     import 'package:flutter/material.dart';

      class MyScroll extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          return new MaterialApp(
            title: 'Flutter Demo',
            theme: new ThemeData(
              primarySwatch: Colors.blue,
            ),
            home: new MyHomePage(title: 'Canvas Scroller'),
          );
        }
      }
      class MyHomePage extends StatefulWidget {
        MyHomePage({Key key, this.title}) : super(key: key);
        final String title;

        @override
        _MyHomePageState createState() => new _MyHomePageState();
      }
      class _MyHomePageState extends State<MyHomePage> {
        @override
        Widget build(BuildContext context) {
          final width = MediaQuery.of(context).size.width;
          final height = MediaQuery.of(context).size.height;
          return new Scaffold(
            appBar: new AppBar(
              title: new Text(widget.title),
            ),
            body: new Center(
              child: new SingleChildScrollView(
                scrollDirection: Axis.horizontal,
                child: new CustomPaint(
                  painter: new MyCanvasView(),
                  size: new Size(width*2, height/2),
                ),
              ),
            ),
          );
        }
      }

      class MyCanvasView extends CustomPainter{
        @override
        void paint(Canvas canvas, Size size) {
          var paint = new Paint();
          paint..shader = new LinearGradient(colors: [Colors.yellow[700], Colors.redAccent],
             begin: Alignment.centerRight, end: Alignment.centerLeft).createShader(new Offset(0.0, 0.0)&size);
          canvas.drawRect(new Offset(0.0, 0.0)&size, paint);
          var path = new Path();
          path.moveTo(0.0, size.height);
          path.lineTo(1*size.width/4, 0*size.height/4);
          path.lineTo(2*size.width/4, 2*size.height/4);
          path.lineTo(3*size.width/4, 0*size.height/4);
          path.lineTo(4*size.width/4, 4*size.height/4);
          canvas.drawPath(path, new Paint()..color = Colors.yellow ..strokeWidth = 4.0 .. style = PaintingStyle.stroke);
        }

        @override
        bool shouldRepaint(CustomPainter oldDelegate) {
          return false;
        }

      }
Share:
3,737
colin
Author by

colin

Updated on December 06, 2022

Comments

  • colin
    colin over 1 year

    I'm a experienced iOS developer, but completely new to the Flutter. Right now I'm facing a problem with ScrollView in Flutter.

    What I want to achieve is building a large scrollable canvas. I did it on iOS before, you can see the screenshot here.

    iOS UI

    The canvas is a big UIScrollView, and each subview on the canvas is draggable, so I can place them at will. Even if the text is very long, I can scroll the canvas to see the full content. Now I need to do the same thing using Flutter.

    Currently, I can only drag the text widgets in Flutter. But the parent widget is not scrollable. I know I need to use a scrollable widget in Flutter to achieve the same result, but I just can't make it work. Here's the code I currently have.

    void main() {
      //debugPaintLayerBordersEnabled = true;
      //debugPaintSizeEnabled = true;
      runApp(new MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
            title: 'Flutter Demo',
            theme: new ThemeData(
            primarySwatch: Colors.indigo,
          ),
          home: new MyHomePage(title: 'Flutter Demo Drag Box'),
        );
      }
    }
    
    class MyHomePage extends StatelessWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(
          title: new Text(title),
        ),
        body: DragBox(Offset(0.0, 0.0)));
      }
    }
    
    class DragBox extends StatefulWidget {
      final Offset position; // widget's position
      DragBox(this.position);
    
      @override
      _DragBoxState createState() => new _DragBoxState();
    }
    
    class _DragBoxState extends State<DragBox> {
      Offset _previousOffset;
      Offset _offset;
      Offset _position;
    
      @override
      void initState() {
        _offset = Offset.zero;
        _previousOffset = Offset.zero;
        _position = widget.position;
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return new Container(
          constraints: BoxConstraints.expand(),
          color: Colors.white24,
          child: Stack(
            children: <Widget>[
            buildDraggableBox(1, Colors.red, _offset)
          ],
        )
      );
    }
    
    Widget buildDraggableBox(int boxNumber, Color color, Offset offset) {
      print('buildDraggableBox $boxNumber !');
      return new Stack(
        children: <Widget>[
          new Positioned(
            left: _position.dx,
            top: _position.dy,
            child: Draggable(
              child: _buildBox(color, offset),
              feedback: _buildBox(color, offset),
              //childWhenDragging: _buildBox(color, offset, onlyBorder: true),
              onDragStarted: () {
                print('Drag started !');
                setState(() {
                  _previousOffset = _offset;
                });
                print('Start position: $_position}');
              },
              onDragCompleted: () {
                print('Drag complete !');
              },
              onDraggableCanceled: (Velocity velocity, Offset offset) {
                // update position here
                setState(() {
                  Offset _offset = Offset(offset.dx, offset.dy - 80);
                  _position = _offset;
                  print('Drag canceled position: $_position');
                });
              },
            ),
          )
        ],
      );
    }
    
    Widget _buildBox(Color color, Offset offset, {bool onlyBorder: false}) {
      return new Container(
        child: new Text('Flutter widget',
          textAlign: TextAlign.center,
          style: new TextStyle(fontWeight: FontWeight.bold, fontSize: 25.0)),
        );
      }
    }
    

    Any suggestions or code samples would be really helpful to me.

    PS: Please forget about the rulers on the screenshot, it's not the most important thing to me right now. I just need a big scrollable canvas now.