3

I'm working on a paint like application where lines and shapes can be drawn using tapped coordinates. I need to get the angle or degree of the lines drawn in my canvas. Have used CustomPaint to draw the lines and XGestureDetector to get the coordinates onTap.

enter image description here

I Need to achieve the above.

Here's my code,

class LandingScreen extends State<MyApp> {
  LandingScreen(List<List<Offset>> connectionList);

  @override
  void initState() {
    XGesture();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    late List<List<Offset>> _connectionList;
    _connectionList = [[Offset(104.0, 242.6), Offset(101.4, 559.3)], [Offset(238.9, 238.6), Offset(229.8, 440.4)]];

    String dropdownvalue = 'Square';
    var items = ['Square', ' Circle ', ' Rectangle ', ' Circle ',];

    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body:
        XGesture(),
      ),
    );
  }
}

class XGesture extends StatefulWidget {
  late List<List<Offset>> connectionListRedo = [];

  @override
  _XGestureState createState() => _XGestureState();
}

class _XGestureState extends State<XGesture> {
  late MoveEvent pos1;
  late MoveEvent pos2;
  late List<List<Offset>> _connectionList;
  late List<List<Offset>> connectionListRedo1;

  _XGestureState();

  @override
  void initState() {
    onMoveStart;
    pos1 = MoveEvent(Offset(0.0, 0.0), Offset(0.0, 0.0), 0);
    pos2 = MoveEvent(Offset(0.0, 0.0), Offset(0.0, 0.0), 0);
    _connectionList = [];
    onMoveEnd;
    LandingScreen(_connectionList);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    String dropdownvalue = 'Square';
    var items = [
      'Square',
      ' Circle ',
      ' Rectangle ',
      ' Circle ',
    ];
    String dropdownvalue1 = 'Teams';
    var items1 = [
      'Teams',
      ' Sluggers ',
      ' Lakes ',
      ' Add More ',
    ];

    final x = Angle.degrees(180.0);
    debugPrint('Angle of x : ${x.degrees}');

    return XGestureDetector(
      child: Material(
        color: Colors.black87,
        child: Center(
            child: CustomPaint(
          painter: LinePainter(pos1.localPos, pos2.localPos, _connectionList),
          child:SingleChildScrollView(
            scrollDirection: Axis.horizontal,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.start,
              children: <Widget>[
                Align(
                  alignment: FractionalOffset.bottomLeft,
                  child:
                Container(
                  padding: EdgeInsets.only(top: 0.5,bottom: 0.5,left: 0.0,right: 0.0),
                  decoration:BoxDecoration(
                      borderRadius:BorderRadius.circular(5),
                    border: Border.all(color: Colors.green, width: 4),
                    color: Color(0xDC063280),
                  ),
                  child: TextButton.icon(
                      label: const Text('Undo', textAlign: TextAlign.justify, style: TextStyle(fontSize: 20, color: Colors.white),),
                      icon: Icon(Icons.undo, color: Colors.white,),
                      onPressed: () {
                        widget.connectionListRedo.add(_connectionList.last);
                        debugPrint('Not Removed Offset List value: ${widget.connectionListRedo}');
                        _connectionList.removeLast();
                        debugPrint('Removed Offset List value: $_connectionList');
                        print('Pressed Undo btn');
                      }),
                ),
        ),
                Align(
                  alignment: FractionalOffset.bottomLeft,
                  child:
                  Container(
                    padding: EdgeInsets.only(top: 0.5,bottom: 0.5,left: 0.0,right: 0.0),
                    decoration:BoxDecoration(
                        borderRadius:BorderRadius.circular(5),
                        border: Border.all(color: Colors.green, width: 4),
                        color: Color(0xDC063280),
                    ),
                    child: TextButton.icon(
                        label: const Text('Redo', textAlign: TextAlign.justify, style: TextStyle(fontSize: 20, color: Colors.white),),
                        icon: Icon(Icons.redo, color: Colors.white,),
                        onPressed: () {
                          debugPrint('Not Removed Offset List value in Redo: ${widget.connectionListRedo}');
                          _connectionList.add(widget.connectionListRedo.last);
                          widget.connectionListRedo.removeLast();
                          debugPrint('Retrieved Offset value Redo: $_connectionList');
                          print('Pressed Undo btn');
                        }),
                  ),
                ),
                Align(
                  alignment: FractionalOffset.bottomLeft,
                  child:
                  Container(
                      padding: EdgeInsets.only(top: 0.5,bottom: 0.5,left: 0.0,right: 0.0),
                      decoration:BoxDecoration(
                        borderRadius:BorderRadius.circular(5),
                        border: Border.all(color: Colors.green, width: 4),
                        color: Color(0xDC063280),
                      ),
                      child:
                      TextButton.icon(
                          label: const Text('Line', textAlign: TextAlign.justify, style: TextStyle(fontSize: 20, color: Colors.white),),
                          icon: Icon(Icons.create, color: Colors.white),
                          onPressed: () {
                            print('Pressed Line btn');
                          })
                  ),
                ),
                Align(
                  alignment: FractionalOffset.bottomLeft,
                  child:
                  Container(
                    padding: EdgeInsets.only(top: 0.5,bottom: 0.5,left: 3.5,right: 0.0),
                      decoration:BoxDecoration(
                          borderRadius:BorderRadius.circular(5),
                        border: Border.all(color: Colors.green, width: 4),
                        color: Color(0xDC063280),
                      ),
                      child:
                      DropdownButton(
                        icon: const Icon(Icons.arrow_drop_down, color: Colors.white, size: 30,),
                        style: const TextStyle(
                          color: Colors.white,
                          fontSize: 20,
                        ),
                        value: dropdownvalue,
                        dropdownColor: Colors.redAccent,
                        items: items.map((String items) {
                        return DropdownMenuItem(child: Text(items),
                        value: items,
                        );
                      }).toList(),
                        onChanged: (String? value){
                        setState(() {
                          dropdownvalue = value!;
                        });
                        },
                      ),
                  ),
                ),
                Align(
                  alignment: FractionalOffset.bottomLeft,
                  child:
                  Container(
                    padding: EdgeInsets.only(top: 0.5,bottom: 0.5,left: 0.0,right: 0.0),
                    decoration:BoxDecoration(
                        borderRadius:BorderRadius.circular(5),
                      border: Border.all(color: Colors.green, width: 4),
                      color: Color(0xDC063280),
                    ),
                    child: TextButton.icon(
                        label: const Text('My Videos', textAlign: TextAlign.justify, style: TextStyle(fontSize: 20, color: Colors.white),),
                        icon: Icon(Icons.image, color: Colors.white,),
                        onPressed: () {
                          print('Pressed Video btn');
                        }),
                  ),
                ),
                Align(
                  alignment: FractionalOffset.bottomLeft,
                  child:
                  Container(
                      padding: EdgeInsets.only(top: 0.5,bottom: 0.5,left: 0.0,right: 0.0),
                      decoration:BoxDecoration(
                          borderRadius:BorderRadius.circular(5),
                        border: Border.all(color: Colors.green, width: 4),
                        color: Color(0xDC063280),

                      ),
                      child:
                      TextButton.icon(
                          label: const Text('Notifications', textAlign: TextAlign.justify, style: TextStyle(fontSize: 20, color: Colors.white),),
                          // icon: Icon(Icons.align_vertical_center_sharp, color: Colors.white),
                          icon: Icon(Icons.notifications, color: Colors.white),

                          onPressed: () {
                            print('Pressed Notifications btn');
                          })
                  ),
                ),
                Align(
                  alignment: FractionalOffset.bottomLeft,
                  child:
                  Container(
                    padding: EdgeInsets.only(top: 0.5,bottom: 0.5,left: 3.5,right: 0.0),
                    decoration:BoxDecoration(
                        borderRadius:BorderRadius.circular(5),
                        border: Border.all(color: Colors.redAccent, width: 4),
                        color:Colors.black87
                    ),
                    child:
                    DropdownButton(
                      icon: const Icon(Icons.arrow_drop_down, color: Colors.white, size: 30,),
                      style: const TextStyle(
                        color: Colors.white,
                        fontSize: 20,
                      ),
                      value: dropdownvalue1,
                      dropdownColor: Colors.redAccent,
                      items: items1.map((String items) {
                        return DropdownMenuItem(child: Text(items),
                          value: items,
                        );
                      }).toList(),
                      onChanged: (String? value){
                        setState(() {
                          dropdownvalue1 = value!;
                        });
                      },
                    ),
                  ),
                ),

              ],
            ),
          ),
        )),
      ),
      doubleTapTimeConsider: 300,
      longPressTimeConsider: 350,
      onTap: onTap,
      onMoveStart: onMoveStart,
      onMoveEnd: onMoveEnd,
      onMoveUpdate: onMoveUpdate,
      onScaleStart: onScaleStart,
      onScaleUpdate: onScaleUpdate,
      onScaleEnd: onScaleEnd,
      bypassTapEventOnDoubleTap: false,
    );

  }

  void onScaleEnd() {
    setLastEventName('onScaleEnd');
    print('onScaleEnd');
  }

  void onScaleStart(initialFocusPoint) {
    setLastEventName('onScaleStart');
    print('onScaleStart - initialFocusPoint: ' + initialFocusPoint.toString());
  }

  void onLongPress(pointer, localPos, position) {
    setLastEventName('onLongPress');
    print('onLongPress - pos: ' + localPos.toString());
  }

  void onDoubleTap(localPos, position) {
    setLastEventName('onDoubleTap');
    print('onDoubleTap - pos: ' + localPos.toString());

    _connectionList.removeLast();
  }

  void onTap(event) {
    setLastEventName('onTap');
    print('onTap - pos: ' + event.localPos.toString());
  }

  Future<void> onMoveUpdate(MoveEvent event) async {
    setLastEventName('onMoveUpdate');
    print('onMoveUpdate - pos: ${event.localPos} delta: ${event.delta}');
  }

  void onMoveStart(localPos) {
    setLastEventName('onMoveStart');
    print('onMoveStart - pos: $localPos');
    pos1 = localPos;
    debugPrint('pos1 Local pos: ${pos1.localPos}');
    debugPrint('pos1 delta pos: ${pos1.delta}');
  }

  void onMoveEnd(localPos) {
    setLastEventName('onMoveEnd');
    print('onMoveEnd - pos: $localPos');
    pos2 = localPos;
    debugPrint('pos2 Local pos: ${pos2.localPos}');
    debugPrint('position1 ${pos1.localPos}, position2 ${pos2.localPos}');

    List<Offset> _offsets = [];
    _offsets.add(pos1.localPos);
    _offsets.add(pos2.localPos);
    _connectionList.add(_offsets);

    widget.connectionListRedo = [];

    debugPrint('Offset List value: ${_offsets}, List: $_connectionList');
    LinePainter(pos1.localPos, pos2.localPos, _connectionList);
  }

  void onScaleUpdate(ScaleEvent event) {
    setLastEventName('onScaleUpdate');
    print(
        'onScaleUpdate - changedFocusPoint:  ${event.focalPoint} ; scale: ${event.scale} ;Rotation: ${event.rotationAngle}');
  }
}

Since I'm new to Flutter, there'll be some unwanted code. Please ignore those.

Terin Tittu
  • 143
  • 1
  • 1
  • 15
  • see `Offset.direction`, the official docs say: *"The angle of this offset as radians clockwise from the positive x-axis, in the range -pi to pi, assuming positive values of the x-axis go to the right and positive values of the y-axis go down."* – pskink Feb 15 '22 at 06:48
  • @pskink I tried to follow the official documentation but couldn't understand it. Can you please elaborate on this concept and how I can implement this in my code. Thanks in advance – Terin Tittu Feb 16 '22 at 06:28
  • 1
    most likely you think that it is represented as degrees - this is not true: `Offset.direction` is represented as [radians](https://en.wikipedia.org/wiki/Radian) - for conversions between degrees / radians see [this](https://en.wikipedia.org/wiki/Radian#Conversions) – pskink Feb 16 '22 at 06:31
  • @pskink so I need to use (radians * 180)/PI to get the degrees. Now I get it. Thanks – Terin Tittu Feb 16 '22 at 06:33

0 Answers0