flutter_shogi_board 0.0.5

  • Readme
  • Changelog
  • Example
  • Installing
  • 48

flutter_shogi_board #

A shogi board widget for Flutter. This widget can be used in conjunction with shogi to render static game board positions, tsume problems or shogi castles.

Shogi (将棋) is a two-player strategy board game native to Japan, belonging to the same family as chess and xiangqi.

Presently the package is very basic in which it can determine the static board position for a given game and move pieces from one position to another. As it is still highly experimental, 0.0.x versioning is used. shogi is also in active development and similarly versioned.

Getting Started #

Import the package #

To import this package, simply add flutter_shogi_board as a dependency in pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  flutter_shogi_board:

Note that this package requires dart >= 2.6.0.

Example #

import 'package:flutter/material.dart';
import 'package:flutter_shogi_board/flutter_shogi_board.dart';
import 'package:shogi/shogi.dart';

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        body: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Center(
            child: ShogiBoard(
              gameBoard: ShogiUtils.initialBoard,
            ),
          ),
        ),
      ),
    ),
  );
}

For more information, see the Flutter app in the example directory. This example is also hosted online.

Basic Usage #

ShogiGameBoard #

This widget renders a shogi game board using a GameBoard and ShogiBoardStyle. Unless style.maxSize is set, it fills its maximum size to that of its parent. The board pieces are rendered as text.

ParameterDescription
gameBoardA GameBoard to render.
styleOptional. A style to render the shogi board, defaults to constants listed below.
showPiecesInHandOptional. Whether pieces in hand should be shown, defaults to true.

ShogiBoardStyle #

A model used to paint a ShogiBoard.

ParameterDescription
maxSizeThe maximum size of the board. Defaults to double.infinity.
pieceColorOptional. The standard piece color, defaults to black.
promotedPieceColorOptional. The promoted piece color, defaults to red.
cellColorOptional. The board cell background color, defaults to transparent.
borderColorOptional. The board cell background color, defaults to gray.
usesJapaneseOptional. Whether japanese characters or english letters are displayed, defaults to true.
showCoordIndicatorsOptional. Whether board coordinate indicators should be shown, defaults to true.
coordIndicatorTypeOptional. The type of coordinate indicators show, defaults to CoordIndicatorType.japanese.

DefaultShogiBoardStyle #

The ShogiBoardStyle to apply to descendant ShogiBoard widgets without an explicit style.

DefaultShogiBoardStyle(
  style: ShogiBoardStyle(
    cellColor: BoardColors.brown,
  ),
  child: MaterialApp(
    ...

Importing a Game Board #

Please see shogi API - Importing a Game Board for full information on how to import a game board. Here are two visual examples:

Future Plans #

This package grew out of my desired to visualize shogi castles in Flutter, and with no game board widget or even a shogi engine available, I decided to roll my own.

For the future I would like to utilize this widget not just for displaying static game boards, but also for tsume problems, thus user interaction may be considered.

Raising Issues and Contributing #

Please report bugs and issues, and raise feature requests on GitHub.

To contribute, submit a PR with a detailed description and tests, if applicable.

[0.0.5] - 11/03/2020 #

  • Updated to use shogi 0.0.3. Raises sdk version to >= 2.6.
  • Added ability to render pieces in hand for each player.
  • Updated the example to demonstrate castle building, a static tsume (with pieces in hand) and a proverb. This example is now compatible with web.
  • Added ShogiBoardStyle and DefaultShogiBoardStyle.
  • Updated CoordIndicatorCell and Piece text to adaptive to containing cell sizes.

[0.0.4] - 10/02/2020 #

  • Fixed shogi package version to use 0.0.2.
  • Added ability to show coordinate indicators on the board.
  • Updated example to show the animation of building a castle.

[0.0.3] - 13/10/2019 #

  • Moved business logic components from this package to new shogi package.

[0.0.2] - 06/10/2019 #

  • Fixed meta package version conflict ^1.1.7 with Flutter: 1.7.8+hotfix.4.

[0.0.1] - 06/10/2019 #

  • Initial release of flutter_shogi_board package for Flutter.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_shogi_board/flutter_shogi_board.dart';
import 'package:shogi/shogi.dart';

void main() {
  runApp(
    MaterialApp(
      home: _HomeScreen(),
    ),
  );
}

class _HomeScreen extends StatelessWidget {
  final Map<String, Function(BuildContext)> routes = {
    'Yagura castle building animation': (context) => _showPage(context, _CastleBuildingAnimation()),
    'Tsume (5手詰)': (context) => _showPage(context, _Tsume()),
    'Proverb': (context) => _showPage(context, _Proverb()),
  };

  static void _showPage(BuildContext context, Widget page) =>
      Navigator.of(context).push(MaterialPageRoute(builder: (_) => page));

  _HomeScreen({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('flutter_shogi_board'),
      ),
      body: ListView.builder(
        itemCount: routes.length,
        itemBuilder: (_, index) => ListTile(
          title: Text(routes.keys.toList()[index]),
          trailing: Icon(Icons.navigate_next),
          onTap: () => routes.values.toList()[index](context),
        ),
      ),
    );
  }
}

class _CastleBuildingAnimation extends StatefulWidget {
  _CastleBuildingAnimation({Key key}) : super(key: key);

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

class _CastleBuildingAnimationState extends State<_CastleBuildingAnimation> {
  List<Move> moves;
  GameBoard gameBoard;
  bool _isDisposed = false;

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

    final game = """
1: ☗P77-76
2: ☗S79-68
3: ☗S68-77
4: ☗G69-78
5: ☗P57-56
6: ☗K59-69
7: ☗G49-58
8: ☗B88-79
9: ☗P67-66
10: ☗G58-67
11: ☗B79-68
12: ☗K69-79
13: ☗K79-88
""";
    moves = CustomNotationConverter().movesFromFile(game);
    gameBoard = ShogiUtils.initialBoard;

    playSequence();
  }

  @override
  void dispose() {
    _isDisposed = true;

    super.dispose();
  }

  Future<void> playSequence() async {
    final duration = Duration(seconds: 2);

    await Future.delayed(duration);

    for (final move in moves) {
      if (_isDisposed) {
        return;
      }

      setState(
        () => gameBoard = GameEngine.makeMove(gameBoard, move),
      );

      await Future.delayed(duration);
    }

    if (_isDisposed) {
      return;
    }

    setState(
      () => gameBoard = ShogiUtils.stringArrayToGameBoard(StaticGameBoards.yagura),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Center(
          child: ShogiBoard(
            gameBoard: gameBoard,
            showPiecesInHand: false,
          ),
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final boardPieces = [
      ...ShogiUtils.stringArrayToBoardPiecesArray([
        '☗:S-14',
        '☗:+R-53',
        '☖:K-24',
        '☖:G-16',
      ]),
      BoardPiece(player: PlayerType.sente, pieceType: PieceType.gold, position: null),
      BoardPiece(player: PlayerType.sente, pieceType: PieceType.silver, position: null),
    ];
    final gameBoard = GameBoard(boardPieces: boardPieces);

    return Scaffold(
      appBar: AppBar(),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Center(
          child: ShogiBoard(
            gameBoard: gameBoard,
            style: ShogiBoardStyle(
              cellColor: BoardColors.brown,
              showCoordIndicators: false,
            ),
          ),
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Avoid a Sitting King'),
      ),
      body: DefaultShogiBoardStyle(
        style: ShogiBoardStyle(
          maxSize: 400,
          coordIndicatorType: CoordIndicatorType.arabic,
        ),
        child: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Container(height: 16),
                Text(
                    'It is extremely dangerous to start fighting with the King sitting on the original square. In Diagram 1, Black has already advanced a Silver onto 4f with his King in the original position. If he wants to launch an attack from here, how would he play?'),
                Container(height: 16),
                _SFENBoard(
                  sfenString: 'ln3k1nl/1r3bg2/p1ppsgspp/1p2ppp2/7P1/2PPPSP2/PPS2P2P/2G1G2R1/LNB1k2NL b -',
                  label: 'Diagram 1',
                  // showPiecesInHand: false,
                ),
                Container(height: 16),
                _MovesList(
                  moves: [
                    'P-3e',
                    'Px3e',
                    'Sx3e',
                    'P*3d',
                    'P-2d',
                    'Px2d',
                    'Sx2d',
                    'Sx2d',
                    'Bx2d',
                    'Bx2d',
                    'Rx2d',
                  ],
                  playerFirstMove: PlayerType.sente,
                ),
                Container(height: 16),
                _SFENBoard(
                  sfenString: 'ln3k1nl/1r4g2/p1ppsg2p/1p2pppR1/9/2PPP4/PPS2P2P/2G1G4/LN2k2NL b bspBS2P',
                  label: 'Diagram 2',
                  showPiecesInHand: true,
                ),
                Container(height: 16),
                Text(
                  '''So far, Black's climbing Silver appears to have made a point. But White has a devastating move to play here.''',
                ),
                Container(height: 8),
                _MovesList(
                  moves: [
                    'B*1e',
                  ],
                  playerFirstMove: PlayerType.gote,
                ),
                Container(height: 8),
                Text(
                    'You cannot be too careful when you have a sitting King. Black had to play K6i first in this case. Then the attack would have been successful.'),
                Container(height: 8),
                Text(
                  'Content taken from http://www.shogi.net/kakugen/.',
                  style: TextStyle(
                    fontSize: 10,
                  ),
                ),
                Container(height: 8),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class _SFENBoard extends StatelessWidget {
  final String sfenString;
  final String label;
  final bool showPiecesInHand;

  const _SFENBoard({
    @required this.sfenString,
    this.label,
    this.showPiecesInHand = true,
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        children: <Widget>[
          ShogiBoard(
            gameBoard: ShogiUtils.sfenStringToGameBoard(sfenString),
            showPiecesInHand: showPiecesInHand,
          ),
          if (label != null)
            Column(
              children: <Widget>[
                if (!showPiecesInHand) Container(height: 4),
                Text(label),
              ],
            ),
        ],
      ),
    );
  }
}

class _MovesList extends StatelessWidget {
  final List<String> moves;
  final PlayerType playerFirstMove;

  const _MovesList({
    @required this.moves,
    this.playerFirstMove = PlayerType.sente,
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Wrap(
        spacing: 4.0,
        runSpacing: 4.0,
        children: <Widget>[
          for (int i = 0; i < moves.length; i++)
            Text((i % 2 == 0 && playerFirstMove == PlayerType.sente ? BoardConfig.sente : BoardConfig.gote) + moves[i]),
        ],
      ),
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  flutter_shogi_board: ^0.0.5

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:flutter_shogi_board/flutter_shogi_board.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
0
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
90
Overall:
Weighted score of the above. [more]
48
Learn more about scoring.

We analyzed this package on Mar 27, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.7.1
  • pana: 0.13.6
  • Flutter: 1.12.13+hotfix.8

Health suggestions

Format lib/src/widgets/coord_indicator_cell.dart.

Run flutter format to format lib/src/widgets/coord_indicator_cell.dart.

Format lib/src/widgets/default_shogi_board_style.dart.

Run flutter format to format lib/src/widgets/default_shogi_board_style.dart.

Format lib/src/widgets/piece_in_hand.dart.

Run flutter format to format lib/src/widgets/piece_in_hand.dart.

Format lib/src/widgets/shogi_board.dart.

Run flutter format to format lib/src/widgets/shogi_board.dart.

Maintenance suggestions

Package is pre-v0.1 release. (-10 points)

While nothing is inherently wrong with versions of 0.0.*, it might mean that the author is still experimenting with the general direction of the API.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.6.0 <3.0.0
auto_size_text ^2.1.0 2.1.0
flutter 0.0.0
meta ^1.1.6 1.1.8
shogi 0.0.3 0.0.3
Transitive dependencies
collection 1.14.11 1.14.12
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test