liuyuqi-dellpc 1 year ago
parent
commit
9d22a17bdd
4 changed files with 396 additions and 10 deletions
  1. 11 5
      lib/pages/home_page.dart
  2. 6 5
      lib/routes.dart
  3. 3 0
      lib/utils/game_manager.dart
  4. 376 0
      lib/views/game_board.dart

+ 11 - 5
lib/pages/home_page.dart

@@ -1,6 +1,8 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_chinese_chees/utils/game_manager.dart';
+import 'package:flutter_chinese_chees/views/game_board.dart';
 
 
-/// Description: 主页
+/// Description: 游戏界面
 /// Time       : 04/28/2023 Friday
 /// Time       : 04/28/2023 Friday
 /// Author     : liuyuqi.gov@msn.cn
 /// Author     : liuyuqi.gov@msn.cn
 class HomePage extends StatefulWidget {
 class HomePage extends StatefulWidget {
@@ -11,6 +13,8 @@ class HomePage extends StatefulWidget {
 }
 }
 
 
 class _HomePageState extends State<HomePage> {
 class _HomePageState extends State<HomePage> {
+  final GameManager gamer = GameManager();
+
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     return Scaffold(
     return Scaffold(
@@ -35,10 +39,12 @@ class _HomePageState extends State<HomePage> {
         UserAccountsDrawerHeader(
         UserAccountsDrawerHeader(
             accountName: Text("张三"), accountEmail: Text(""))
             accountName: Text("张三"), accountEmail: Text(""))
       ])),
       ])),
-      body: SafeArea(
-          child: Column(
-        children: [],
-      )),
+      body: GameBoard(),
     );
     );
   }
   }
+
+  @override
+  void dispose() {
+    super.dispose();
+  }
 }
 }

+ 6 - 5
lib/routes.dart

@@ -1,4 +1,7 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_chinese_chees/pages/home_page.dart';
+import 'package:flutter_chinese_chees/pages/login_page.dart';
+import 'package:flutter_chinese_chees/pages/register_page.dart';
 import 'package:flutter_chinese_chees/pages/splash_page.dart';
 import 'package:flutter_chinese_chees/pages/splash_page.dart';
 
 
 class Routes {
 class Routes {
@@ -11,13 +14,11 @@ class Routes {
   static Route onGenerateRoute(RouteSettings settings) {
   static Route onGenerateRoute(RouteSettings settings) {
     switch (settings.name) {
     switch (settings.name) {
       case home:
       case home:
-        return MaterialPageRoute(builder: (_) => const SplashPage());
+        return MaterialPageRoute(builder: (_) => const HomePage());
       case login:
       case login:
-        return MaterialPageRoute(builder: (_) => const SplashPage());
+        return MaterialPageRoute(builder: (_) => const LoginPage());
       case register:
       case register:
-        return MaterialPageRoute(builder: (_) => const SplashPage());
-      case logout:
-        return MaterialPageRoute(builder: (_) => const SplashPage());
+        return MaterialPageRoute(builder: (_) => const RegisterPage());
       case splash:
       case splash:
         return MaterialPageRoute(builder: (_) => const SplashPage());
         return MaterialPageRoute(builder: (_) => const SplashPage());
       default:
       default:

+ 3 - 0
lib/utils/game_manager.dart

@@ -0,0 +1,3 @@
+class GameManager {
+  
+}

+ 376 - 0
lib/views/game_board.dart

@@ -0,0 +1,376 @@
+import 'dart:async';
+import 'dart:io';
+
+import 'package:fast_gbk/fast_gbk.dart';
+import 'package:file_picker/file_picker.dart';
+import 'package:shirne_dialog/shirne_dialog.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:universal_html/html.dart' as html;
+
+import 'global.dart';
+import 'setting.dart';
+import 'components/game_bottom_bar.dart';
+import 'models/play_mode.dart';
+import 'widgets/game_wrapper.dart';
+import 'models/game_manager.dart';
+import 'components/play.dart';
+import 'components/edit_fen.dart';
+
+/// 游戏页面
+class GameBoard extends StatefulWidget {
+  const GameBoard({Key? key}) : super(key: key);
+
+  @override
+  State<GameBoard> createState() => _GameBoardState();
+}
+
+class _GameBoardState extends State<GameBoard> {
+  GameManager gamer = GameManager.instance;
+  PlayMode? mode;
+
+  @override
+  void initState() {
+    super.initState();
+  }
+
+  Widget selectMode() {
+    final maxHeight = MediaQuery.of(context).size.height;
+
+    return Center(
+      child: SizedBox(
+        height: maxHeight * 0.6,
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.spaceAround,
+          children: [
+            ElevatedButton.icon(
+              onPressed: () {
+                setState(() {
+                  mode = PlayMode.modeRobot;
+                });
+              },
+              icon: const Icon(Icons.android),
+              label: Text(context.l10n.modeRobot),
+            ),
+            ElevatedButton.icon(
+              onPressed: () {
+                MyDialog.toast(
+                  context.l10n.featureNotAvailable,
+                  iconType: IconType.error,
+                );
+              },
+              icon: const Icon(Icons.wifi),
+              label: Text(context.l10n.modeOnline),
+            ),
+            ElevatedButton.icon(
+              onPressed: () {
+                setState(() {
+                  mode = PlayMode.modeFree;
+                });
+              },
+              icon: const Icon(Icons.map),
+              label: Text(context.l10n.modeFree),
+            ),
+            if (kIsWeb)
+              TextButton(
+                onPressed: () {
+                  var link =
+                      html.window.document.getElementById('download-apk');
+                  if (link == null) {
+                    link = html.window.document.createElement('a');
+                    link.style.display = 'none';
+                    link.setAttribute('id', 'download-apk');
+                    link.setAttribute('target', '_blank');
+                    link.setAttribute('href', 'chinese-chess.apk');
+                    html.window.document
+                        .getElementsByTagName('body')[0]
+                        .append(link);
+                  }
+                  link.click();
+                },
+                child: const Text('Download APK'),
+              ),
+          ],
+        ),
+      ),
+    );
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: Text(context.l10n.appTitle),
+        leading: Builder(
+          builder: (BuildContext context) {
+            return IconButton(
+              icon: const Icon(Icons.menu),
+              tooltip: context.l10n.openMenu,
+              onPressed: () {
+                Scaffold.of(context).openDrawer();
+              },
+            );
+          },
+        ),
+        actions: mode == null
+            ? null
+            : [
+                IconButton(
+                  icon: const Icon(Icons.swap_vert),
+                  tooltip: context.l10n.flipBoard,
+                  onPressed: () {
+                    gamer.flip();
+                  },
+                ),
+                IconButton(
+                  icon: const Icon(Icons.copy),
+                  tooltip: context.l10n.copyCode,
+                  onPressed: () {
+                    copyFen();
+                  },
+                ),
+                IconButton(
+                  icon: const Icon(Icons.airplay),
+                  tooltip: context.l10n.parseCode,
+                  onPressed: () {
+                    applyFen();
+                  },
+                ),
+                IconButton(
+                  icon: const Icon(Icons.airplay),
+                  tooltip: context.l10n.editCode,
+                  onPressed: () {
+                    editFen();
+                  },
+                ),
+                /*IconButton(icon: Icon(Icons.minimize), onPressed: (){
+
+          }),
+          IconButton(icon: Icon(Icons.zoom_out_map), onPressed: (){
+
+          }),
+          IconButton(icon: Icon(Icons.clear), color: Colors.red, onPressed: (){
+            this._showDialog(context.l10n.exit_now,
+                [
+                  TextButton(
+                    onPressed: (){
+                      Navigator.of(context).pop();
+                    },
+                    child: Text(context.l10n.dont_exit),
+                  ),
+                  TextButton(
+                      onPressed: (){
+                        if(!kIsWeb){
+                          Isolate.current.pause();
+                          exit(0);
+                        }
+                      },
+                      child: Text(context.l10n.yes_exit,style: TextStyle(color:Colors.red)),
+                  )
+                ]
+            );
+          })*/
+              ],
+      ),
+      drawer: Drawer(
+        semanticLabel: context.l10n.menu,
+        child: ListView(
+          padding: EdgeInsets.zero,
+          children: [
+            DrawerHeader(
+              decoration: const BoxDecoration(
+                color: Colors.blue,
+              ),
+              child: Center(
+                child: Column(
+                  children: [
+                    Image.asset(
+                      'assets/images/logo.png',
+                      width: 100,
+                      height: 100,
+                    ),
+                    Text(
+                      context.l10n.appTitle,
+                      style: const TextStyle(
+                        color: Colors.white,
+                        fontSize: 24,
+                      ),
+                    ),
+                  ],
+                ),
+              ),
+            ),
+            ListTile(
+              leading: const Icon(Icons.add),
+              title: Text(context.l10n.newGame),
+              onTap: () {
+                Navigator.pop(context);
+                setState(() {
+                  if (mode == null) {
+                    setState(() {
+                      mode = PlayMode.modeFree;
+                    });
+                  }
+                  gamer.newGame();
+                  //mode = null;
+                });
+              },
+            ),
+            ListTile(
+              leading: const Icon(Icons.description),
+              title: Text(context.l10n.loadManual),
+              onTap: () {
+                Navigator.pop(context);
+                if (mode == null) {
+                  setState(() {
+                    mode = PlayMode.modeFree;
+                  });
+                }
+                loadFile();
+              },
+            ),
+            ListTile(
+              leading: const Icon(Icons.save),
+              title: Text(context.l10n.saveManual),
+              onTap: () {
+                Navigator.pop(context);
+                saveManual();
+              },
+            ),
+            ListTile(
+              leading: const Icon(Icons.copy),
+              title: Text(context.l10n.copyCode),
+              onTap: () {
+                Navigator.pop(context);
+                copyFen();
+              },
+            ),
+            ListTile(
+              leading: const Icon(Icons.settings),
+              title: Text(context.l10n.setting),
+              onTap: () {
+                Navigator.pop(context);
+                Navigator.push(
+                  context,
+                  MaterialPageRoute(
+                    builder: (BuildContext context) => const SettingPage(),
+                  ),
+                );
+              },
+            ),
+          ],
+        ),
+      ),
+      body: SafeArea(
+        child: Center(
+          child: mode == null ? selectMode() : PlayPage(mode: mode!),
+        ),
+      ),
+      bottomNavigationBar:
+          (mode == null || MediaQuery.of(context).size.width >= 980)
+              ? null
+              : GameBottomBar(mode!),
+    );
+  }
+
+  void editFen() {
+    Navigator.of(context).push<String>(
+      MaterialPageRoute(
+        builder: (BuildContext context) {
+          return GameWrapper(child: EditFen(fen: gamer.fenStr));
+        },
+      ),
+    ).then((fenStr) {
+      if (fenStr != null && fenStr.isNotEmpty) {
+        gamer.newGame(fenStr);
+      }
+    });
+  }
+
+  Future<void> applyFen() async {
+    final l10n = context.l10n;
+    ClipboardData? cData = await Clipboard.getData(Clipboard.kTextPlain);
+    String fenStr = cData?.text ?? '';
+    TextEditingController filenameController =
+        TextEditingController(text: fenStr);
+    filenameController.addListener(() {
+      fenStr = filenameController.text;
+    });
+
+    final confirmed = await MyDialog.confirm(
+      TextField(
+        controller: filenameController,
+      ),
+      buttonText: l10n.apply,
+      title: l10n.situationCode,
+    );
+    if (confirmed ?? false) {
+      if (RegExp(
+        r'^[abcnrkpABCNRKP\d]{1,9}(?:/[abcnrkpABCNRKP\d]{1,9}){9}(\s[wb]\s-\s-\s\d+\s\d+)?$',
+      ).hasMatch(fenStr)) {
+        gamer.newGame(fenStr);
+      } else {
+        MyDialog.alert(l10n.invalidCode);
+      }
+    }
+  }
+
+  void copyFen() {
+    Clipboard.setData(ClipboardData(text: gamer.fenStr));
+    MyDialog.alert(context.l10n.copySuccess);
+  }
+
+  Future<void> saveManual() async {
+    String content = gamer.manual.export();
+    String filename = '${DateTime.now().millisecondsSinceEpoch ~/ 1000}.pgn';
+    if (kIsWeb) {
+      await _saveManualWeb(content, filename);
+    } else if (Platform.isAndroid || Platform.isIOS) {
+      await _saveManualNative(content, filename);
+    }
+  }
+
+  Future<void> _saveManualNative(String content, String filename) async {
+    final result = await FilePicker.platform.saveFile(
+      dialogTitle: 'Save pgn file',
+      fileName: filename,
+      allowedExtensions: ['pgn'],
+    );
+    if (context.mounted && result != null) {
+      List<int> fData = gbk.encode(content);
+      await File('$result/$filename').writeAsBytes(fData);
+      MyDialog.toast(context.l10n.saveSuccess);
+    }
+  }
+
+  Future<void> _saveManualWeb(String content, String filename) async {
+    List<int> fData = gbk.encode(content);
+    var link = html.window.document.createElement('a');
+    link.setAttribute('download', filename);
+    link.style.display = 'none';
+    link.setAttribute('href', Uri.dataFromBytes(fData).toString());
+    html.window.document.getElementsByTagName('body')[0].append(link);
+    link.click();
+    await Future<void>.delayed(const Duration(seconds: 10));
+    link.remove();
+  }
+
+  Future<void> loadFile() async {
+    FilePickerResult? result = await FilePicker.platform.pickFiles(
+      type: FileType.custom,
+      allowedExtensions: ['pgn', 'PGN'],
+      withData: true,
+    );
+
+    if (result != null && result.count == 1) {
+      String content = gbk.decode(result.files.single.bytes!);
+      if (gamer.isStop) {
+        gamer.newGame();
+      }
+      gamer.loadPGN(content);
+    } else {
+      // User canceled the picker
+    }
+  }
+}