<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: bluenote</title>
    <description>The latest articles on DEV Community by bluenote (@bluenote1982).</description>
    <link>https://dev.to/bluenote1982</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1091281%2Fa732eb2d-726f-4cbd-bcd9-ace252d42702.jpg</url>
      <title>DEV Community: bluenote</title>
      <link>https://dev.to/bluenote1982</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bluenote1982"/>
    <language>en</language>
    <item>
      <title>Question about Isolate in Flutter and match-3 game implementation</title>
      <dc:creator>bluenote</dc:creator>
      <pubDate>Tue, 13 Jun 2023 00:50:23 +0000</pubDate>
      <link>https://dev.to/bluenote1982/question-about-isolate-in-flutter-and-match-3-game-implementation-1efo</link>
      <guid>https://dev.to/bluenote1982/question-about-isolate-in-flutter-and-match-3-game-implementation-1efo</guid>
      <description>&lt;p&gt;I have been making match-3 game for implementing tile matching detection code. I have tried three kind of variations that do the work. The following error occurs each time known solution is tried:&lt;/p&gt;

&lt;p&gt;ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Invalid argument(s): Illegal argument in isolate message: object is unsendable - Library:'package:flutter/src/scheduler/ticker.dart' Class: TickerFuture (see restrictions listed at &lt;code&gt;SendPort.send()&lt;/code&gt; documentation for more information)&lt;/p&gt;

&lt;p&gt;I have experienced as if type mistake which I didn't do, when I copy and paste the same code I wrote then syntax error does not continue. But the error the above is occurring any codes as solution, changing many things to code and reverted back, the file location error occurred changed to scheduler/ticker.dart.&lt;/p&gt;

&lt;p&gt;I know the file is resposible for animation work, but there is no problem at all.&lt;/p&gt;

&lt;p&gt;I am worrying I had to be like Shimomura to resolve this problem, the following code would contain type mistake while I write this asking too.&lt;/p&gt;

&lt;p&gt;The following is code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'dart:async';
import 'dart:isolate';
import 'dart:math';

import 'package:flutter/material.dart';

// ignore: must_be_immutable
class Tile extends StatelessWidget {
  List mBitmap = [
    Image.asset('assets/images/games/match-3/tile0.jpg'),
    Image.asset('assets/images/games/match-3/tile1.jpg'),
    Image.asset('assets/images/games/match-3/tile2.jpg'),
    Image.asset('assets/images/games/match-3/tile3.jpg'),
  ];

  var mBitmapDestroy = Image.asset('assets/images/games/match-3/heart.jpg');

  late int value;
  late int row;
  late int col;

  late double mTileWidth;
  late double mTileHeight;

  Tile({required this.value, required this.row, required this.col, Key? key})
      : super(key: key);

  get image {
    switch (value) {
      case 0:
        return mBitmap[0];
      case 1:
        return mBitmap[1];
      case 2:
        return mBitmap[2];
      case 3:
        return mBitmap[3];
      case 4:
        return mBitmapDestroy;
    }
  }

  get getRow =&amp;gt; row;

  get getCol =&amp;gt; col;

  get getValue =&amp;gt; value;

  setRow(int row) {
    this.row = row;
  }

  setCol(int col) {
    this.col = col;
  }

  setValue(int value) {
    this.value = value;
  }

  @override
  Widget build(BuildContext context) {
    mTileWidth = MediaQuery.of(context).size.width;
    mTileHeight = mTileWidth;

    return SizedBox(
      width: mTileWidth,
      height: mTileHeight,
      child: image,
    );
  }
}

class TileMatchingGamePage extends StatelessWidget {
  const TileMatchingGamePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) =&amp;gt; const TileMatchingGameStatePage();
}

class TileMatchingGameStatePage extends StatefulWidget {
  const TileMatchingGameStatePage({Key? key}) : super(key: key);

  @override
  State&amp;lt;TileMatchingGameStatePage&amp;gt; createState() =&amp;gt;
      _TileMatchingGameStatePage();
}

class _TileMatchingGameStatePage extends State&amp;lt;TileMatchingGameStatePage&amp;gt;
    with SingleTickerProviderStateMixin {
  late List&amp;lt;List&amp;gt; tiles;
  late List&amp;lt;List&amp;gt; toRemove;
  late List&amp;lt;List&amp;lt;Tween&amp;lt;Offset&amp;gt;&amp;gt;&amp;gt; _offsetTweens;
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 400),
    );
  }

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

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    tiles = List.generate(
      7,
      (i) =&amp;gt; List.generate(
        7,
        (j) =&amp;gt; Tile(value: Random().nextInt(4), row: i, col: j),
      ),
    );

    toRemove = List.generate(
      7,
      (i) =&amp;gt; List.generate(
        7,
        (j) =&amp;gt; false,
      ),
    );

    _offsetTweens = List.generate(
      7,
      (i) =&amp;gt; List.generate(
        7,
        (j) =&amp;gt; Tween&amp;lt;Offset&amp;gt;(begin: Offset.zero, end: Offset.zero),
      ),
    );
  }

  int mScore = 0;
  int mCombo = 0;

  bool checkAdjacentTiles() {
    bool bFound = false;

    // 모든 칸 리셋
    for (int x = 0; x &amp;lt; 7; x++) {
      for (int y = 0; y &amp;lt; 7; y++) {
        toRemove[x][y] = false;
      }
    }

    // 전체 순회, 3연속 찾기
    for (int x = 0; x &amp;lt; 7; x++) {
      for (int y = 0; y &amp;lt; 7; y++) {
        int value = tiles[x][y].getValue;

        // 수평 3개가 같으면 제거 대상
        if (x &amp;gt; 0 &amp;amp;&amp;amp; x &amp;lt; 7 - 1) {
          if ((tiles[x - 1][y].getValue == value) &amp;amp;&amp;amp;
              (tiles[x + 1][y].getValue == value)) {
            toRemove[x - 1][y] = true;
            toRemove[x][y] = true;
            toRemove[x + 1][y] = true;
            bFound = true;
          }
        }

        // 수직 3개가 같으면 제거 대상
        if (y &amp;gt; 0 &amp;amp;&amp;amp; y &amp;lt; 7 - 1) {
          if ((tiles[x][y - 1].getValue == value) &amp;amp;&amp;amp;
              (tiles[x][y + 1].getValue == value)) {
            toRemove[x][y - 1] = true;
            toRemove[x][y] = true;
            toRemove[x][y + 1] = true;
            bFound = true;
          }
        }
      }
    }

    mCombo++;

    return bFound;
  }

  void removeTiles(_) {
    setState(() {
      int count = 0;
      for (int x = 0; x &amp;lt; 7; x++) {
        for (int y = 0; y &amp;lt; 7; y++) {
          if (toRemove[x][y]) {
            tiles[x][y] = Tile(value: 4, row: x, col: y);
            count++;
          }
        }
      }

      mScore += (count * mCombo);

      bool bFound = false;
      for (int y = 7 - 1; y &amp;gt;= 0; y--) {
        bFound = false;
        for (int x = 0; x &amp;lt; 7; x++) {
          if (toRemove[x][y]) {
            bFound = true;
            for (int ty = y; ty &amp;gt; 0; ty--) {
              tiles[x][ty].setValue(tiles[x][ty - 1].getValue);
            }
            tiles[x][0].setValue(4);
          }
        }

        if (bFound) y++;
      }
    });

    Future.delayed(const Duration(milliseconds: 600));

    setState(() {
      for (int x = 0; x &amp;lt; 7; x++) {
        for (int y = 0; y &amp;lt; 7; y++) {
          if (tiles[x][y].getValue == 4) {
            tiles[x][y] = Tile(value: Random().nextInt(4), row: x, col: y);
          }
        }
      }
    });
  }

  void makeNewTiles(_) {
    setState(() {
      for (int x = 0; x &amp;lt; 7; x++) {
        for (int y = 0; y &amp;lt; 7; y++) {
          if (tiles[x][y].getValue == 4) {
            tiles[x][y] = Tile(value: Random().nextInt(4), row: x, col: y);
          }
        }
      }
    });
  }

  void updateRowCol() {
    setState(() {
      for (int i = 0; i &amp;lt; 7; i++) {
        for (int j = 0; j &amp;lt; 7; j++) {
          tiles[i][j].setRow(i);
          tiles[i][j].setCol(j);
        }
      }
    });
  }

  /*
  void doTileMatchingWork() {
    if (checkAdjacentTiles()) {
      compute(removeTiles, null);
      compute(makeNewTiles, null);
    }
    updateRowCol();
  }*/

  doTileMatchingWork() async {
    ReceivePort mReceivePort = ReceivePort();
    if (checkAdjacentTiles()) {
      var receivePort = ReceivePort();
      Isolate isolate = await Isolate.spawn&amp;lt;SendPort&amp;gt;(isolateEntry,
          mReceivePort.sendPort);

      SendPort anotherPort = await mReceivePort.first;
      ReceivePort anotherResponseReceiePort = ReceivePort();

      receivePort.listen((data) {
        switch (data) {
          case "makeNewTiles":
            anotherPort.send(["makeNewTiles", anotherResponseReceiePort.sendPort]);
            break;
          case "updateRowCol":
            anotherPort.send(["updateRowCol", anotherResponseReceiePort.sendPort]);
            break;
        }
      });
    }
  }

  void isolateEntry(SendPort mSendPort) async {
    ReceivePort anotherPort = ReceivePort();
    mSendPort.send(anotherPort.sendPort);
    late var _ = "";

    await for (var message in anotherPort) {
      if (message is List) {
        var function = message[0];
        switch (function) {
          case "removeTiles":
            removeTiles(_);
            anotherPort.sendPort.send("makeNewTiles");
            break;
          case "makeNewTiles":
            makeNewTiles(_);
            anotherPort.sendPort.send("updateRowCol");
            break;
          case "updateRowCol":
            updateRowCol();
            break;
        }
      }
    }
  }

  void swapTiles(int i, int j, int k, int l) {
    setState(() {
      final temp = tiles[i][j];
      tiles[i][j] = tiles[k][l];
      tiles[k][l] = temp;
    });
  }

  bool _isAnimating = false;

  void animateAndSwap(int di, int dj, AxisDirection direction) {
    if (_isAnimating) return;
    _isAnimating = true;
    // 아래쪽 코드는 flutter 3.10 (dart 3.0) 이후로 훨씬 간단히 줄일 수 있음.
    final si = di +
        (direction == AxisDirection.up
            ? -1
            : direction == AxisDirection.down
                ? 1
                : 0);
    final sj = dj +
        (direction == AxisDirection.left
            ? -1
            : direction == AxisDirection.right
                ? 1
                : 0);
    final Offset dragOffset =
        direction == AxisDirection.up || direction == AxisDirection.down
            ? Offset(0, direction == AxisDirection.up ? -1 : 1)
            : Offset(direction == AxisDirection.left ? -1 : 1, 0);
    final swapOffset =
        direction == AxisDirection.up || direction == AxisDirection.down
            ? Offset(0, direction == AxisDirection.up ? 1 : -1)
            : Offset(direction == AxisDirection.left ? 1 : -1, 0);
    if (si &amp;lt; 0 || si &amp;gt;= 7 || sj &amp;lt; 0 || sj &amp;gt;= 7) {
      _isAnimating = false;
      return;
    }
    _offsetTweens[di][dj].end = dragOffset;
    _offsetTweens[si][sj].end = swapOffset;
    _controller.reset();
    _controller.forward().whenCompleteOrCancel(() {
      _offsetTweens[di][dj].end = Offset.zero;
      _offsetTweens[si][sj].end = Offset.zero;
      swapTiles(di, dj, si, sj);
      _isAnimating = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
      shrinkWrap: true,
      itemCount: 7 * 7,
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 7,
      ),
      itemBuilder: (BuildContext context, int index) {
        int i = index ~/ 7;
        int j = index % 7;

        return SlideTransition(
          position: _offsetTweens[i][j].animate(_controller),
          child: GestureDetector(
            // 가로 방향으로 드래그할 때 콜백 함수
            onHorizontalDragEnd: (details) {
              // 드래그한 방향에 따라 인접한 타일의 인덱스 설정
              final dx = details.primaryVelocity!;
              if (dx &amp;gt; 0) {
                animateAndSwap(i, j, AxisDirection.right);
                doTileMatchingWork();
              } else if (dx &amp;lt; 0) {
                // 왼쪽으로 드래그하면 왼쪽 타일과 교환
                animateAndSwap(i, j, AxisDirection.left);
                doTileMatchingWork();
              }
            },
            onVerticalDragEnd: (details) {
              // 드래그한 방향에 따라 인접한 타일의 인덱스 설정
              final dy = details.primaryVelocity!;
              if (dy &amp;gt; 0) {
                animateAndSwap(i, j, AxisDirection.down);
                doTileMatchingWork();
              } else if (dy &amp;lt; 0) {
                animateAndSwap(i, j, AxisDirection.up);
                doTileMatchingWork();
              }
            },
            child: tiles[i][j],
          ),
        );
      },
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can you address what is wrong and isolate is legitimate for this code? If not using isolate, app is freezing when it runs. The tile shows 100KB image each, and setState() and mutli for loop and 2-dimensional lists.&lt;/p&gt;

&lt;p&gt;I have made sure the code works even before freezing observed, debugger no longer shows buttons and shortcut keys to test step by step inspection.&lt;/p&gt;

&lt;p&gt;How can I do for?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Flutter's TweenAnimationBuilder Tween animation reverts back when Tween is run again</title>
      <dc:creator>bluenote</dc:creator>
      <pubDate>Sun, 28 May 2023 21:23:39 +0000</pubDate>
      <link>https://dev.to/bluenote1982/flutters-tweenanimationbuilder-tween-animation-reverts-back-when-tween-is-run-again-4o36</link>
      <guid>https://dev.to/bluenote1982/flutters-tweenanimationbuilder-tween-animation-reverts-back-when-tween-is-run-again-4o36</guid>
      <description>&lt;p&gt;Hi,&lt;/p&gt;

&lt;p&gt;I am creating Match-3 game in flutter app, I have used TweenAnimationBuilder for exchanging two adjacent tiles spots.&lt;/p&gt;

&lt;p&gt;The source code is as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'dart:async';
import 'dart:math';

import 'package:flutter/material.dart';

class Tile extends StatelessWidget {
  List mBitmap = [
    Image.asset('assets/images/games/match-3/tile0.jpg'),
    Image.asset('assets/images/games/match-3/tile1.jpg'),
    Image.asset('assets/images/games/match-3/tile2.jpg'),
    Image.asset('assets/images/games/match-3/tile3.jpg'),
  ];

  var mBitmapDestroy = Image.asset('assets/images/games/match-3/heart.jpg');

  Image image = Image.asset("");

  late int value;
  late int row;
  late int col;

  Tile({required this.value, required this.row, required this.col, Key? key})
      : super(key: key);

  Image? getImage() {
    switch (value) {
      case 0:
        return mBitmap[0];
      case 1:
        return mBitmap[1];
      case 2:
        return mBitmap[2];
      case 3:
        return mBitmap[3];
      case 4:
        return mBitmapDestroy;
    }
  }

  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}

class TileMatchingGamePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const TileMatchingGameStatePage();
  }
}

class TileMatchingGameStatePage extends StatefulWidget {
  const TileMatchingGameStatePage({Key? key}) : super(key: key);

  @override
  State&amp;lt;TileMatchingGameStatePage&amp;gt; createState() =&amp;gt;
      _TileMatchingGameStatePage();
}

class _TileMatchingGameStatePage extends State&amp;lt;TileMatchingGameStatePage&amp;gt;
    with SingleTickerProviderStateMixin {
  List&amp;lt;List&amp;lt;int&amp;gt;&amp;gt; board = [
    [
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
    ],
    [
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
    ],
    [
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
    ],
    [
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
    ],
    [
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
    ],
    [
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
    ],
    [
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
    ],
    [
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
      Random().nextInt(4),
    ],
  ];

  late Animation&amp;lt;Offset&amp;gt; _animation;
  late AnimationController _controller;
  Offset _offset = Offset.zero;

  List&amp;lt;List&amp;lt;Tile&amp;gt;&amp;gt; tiles = List.generate(
    7,
    (i) =&amp;gt; List.generate(7, (j) =&amp;gt; Tile(value: 0, row: i, col: j)),
  );

  late final mTileWidth;
  late final mTileHeight;

  @override
  void initState() {
    super.initState();
    mTileWidth = MediaQuery.of(context).size.width / 7;
    mTileHeight = mTileWidth;
    _controller =
        AnimationController(vsync: this, duration: Duration(milliseconds: 300));
    _animation = Tween&amp;lt;Offset&amp;gt;(begin: Offset.zero, end: Offset.zero)
        .animate(_controller);
    _animation.addListener(() {
      setState(() {
        // 현재 오프셋을 애니메이션 값으로 업데이트
        _offset = _animation.value;
      });
    });

    for (int i = 0; i &amp;lt; 7; i++) {
      for (int j = 0; j &amp;lt; 7; j++) {
        switch (board[i][j]) {
          case 0:
            tiles[i][j] = Tile(value: 0, row: i, col: j);
            break;
          case 1:
            tiles[i][j] = Tile(value: 1, row: i, col: j);
            break;
          case 2:
            tiles[i][j] = Tile(value: 2, row: i, col: j);
            break;
          case 3:
            tiles[i][j] = Tile(value: 3, row: i, col: j);
            break;
          case 4:
            tiles[i][j] = Tile(value: 4, row: i, col: j);
            break;
        }
      }
    }
  }

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

  void swapTiles(int i, int j, int k, int l) {
    setState(() {
      var temp = tiles[i][j];
      tiles[i][j] = tiles[k][l];
      tiles[k][l] = temp;
      var temp2 = board[i][j];
      board[i][j] = board[k][l];
      board[k][l] = temp2;
    });
  }

  // 드래그한 타일의 인덱스
  int? dragI;
  int? dragJ;

  // 인접한 타일의 인덱스
  int? swapI;
  int? swapJ;

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
      shrinkWrap: true,
      itemCount: 7 * 7,
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 7,
      ),
      itemBuilder: (BuildContext context, int index) {
        int i = index ~/ 7;
        int j = index % 7;

        Offset offset = Offset.zero;
        if (i == dragI &amp;amp;&amp;amp; j == dragJ) {
          // 드래그한 타일이면 인접한 타일 방향으로 이동
          if (swapI != null &amp;amp;&amp;amp; swapJ != null) {
            offset = Offset(
              ((swapJ! - dragJ!) * mTileWidth).toDouble(),
              ((swapI! - dragI!) * mTileHeight).toDouble(),
            );
          }
        } else if (i == swapI &amp;amp;&amp;amp; j == swapJ) {
          // 인접한 타일이면 드래그한 타일 방향으로 이동
          if (dragI != null &amp;amp;&amp;amp; dragJ != null) {
            offset = Offset(
              ((dragJ! - swapJ!) * mTileWidth).toDouble(),
              ((dragI! - swapI!) * mTileHeight).toDouble(),
            );
          }
        }

        // 애니메이션 적용
        return TweenAnimationBuilder&amp;lt;Offset&amp;gt;(
          tween: Tween(begin: Offset.zero, end: offset),
          duration: Duration(milliseconds: 300),
          builder: (context, value, child) {
            return Transform.translate(
              offset: value,
              child: child,
            );
          },
          child: GestureDetector(
            // 가로 방향으로 드래그할 때 콜백 함수
            onHorizontalDragUpdate: (details) {
              // 드래그한 타일의 인덱스 설정
              dragI = i;
              dragJ = j;

              // 드래그한 방향에 따라 인접한 타일의 인덱스 설정
              if (details.delta.dx &amp;gt; 0) {
                // 오른쪽으로 드래그하면 오른쪽 타일과 교환
                swapI = i;
                swapJ = j + 1;
              } else if (details.delta.dx &amp;lt; 0) {
                // 왼쪽으로 드래그하면 왼쪽 타일과 교환
                swapI = i;
                swapJ = j - 1;
              }

              // 인접한 타일이 유효한 범위에 있으면 교환
              if (swapI != null &amp;amp;&amp;amp;
                  swapJ != null &amp;amp;&amp;amp;
                  swapI! &amp;gt;= 0 &amp;amp;&amp;amp;
                  swapI! &amp;lt; 7 &amp;amp;&amp;amp;
                  swapJ! &amp;gt;= 0 &amp;amp;&amp;amp;
                  swapJ! &amp;lt; 7) {
                swapTiles(dragI!, dragJ!, swapI!, swapJ!);
              }
            },
            onVerticalDragUpdate: (details) {
              // 드래그한 타일의 인덱스 설정
              dragI = i;
              dragJ = j;

              // 드래그한 방향에 따라 인접한 타일의 인덱스 설정
              if (details.delta.dy &amp;gt; 0) {
                swapI = i + 1;
                swapJ = j;
              } else if (details.delta.dy &amp;lt; 0) {
                swapI = i - 1;
                swapJ = j;
              }

              // 인접한 타일이 유효한 범위에 있으면 교환
              if (swapI != null &amp;amp;&amp;amp;
                  swapJ != null &amp;amp;&amp;amp;
                  swapI! &amp;gt;= 0 &amp;amp;&amp;amp;
                  swapI! &amp;lt; 7 &amp;amp;&amp;amp;
                  swapJ! &amp;gt;= 0 &amp;amp;&amp;amp;
                  swapJ! &amp;lt; 7) {
                swapTiles(dragI!, dragJ!, swapI!, swapJ!);
              }
            },
            child: SizedBox(
              width: mTileWidth,
              height: mTileHeight,
              child: tiles[i][j].getImage(),
            ),
          ),
        );
      },
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source code quoted verbose but enough information how it works. It works well in first dragging, exchanges two tiles well. However, when more times dragging, previous exchange reverts back to original spots so four tiles are moving. I think Tween animation run again and again, the original spots reverts back to, so it runs that way. Any ideas how to repair this operation?&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>animation</category>
    </item>
  </channel>
</rss>
