1

所以从过去的几天开始,我一直在试图绕过这个逻辑循环,并尝试了我能做的一切,最后决定在这里发布(因此在这里首次亮相)。我被卡住了,感谢所有帮助。谢谢 !

逻辑:在应用程序中有 2 个container显示白色和黑色的时间,从给定的五个选项中选择 -> 5,10,15,30,60,然后更新在这些容器中显示的时间,使用了一个provider包这和其他一切。

现在我还添加了一个Raised button名为“开关”,按下它应该:

  1. 开始是白人的倒计时
  2. 然后如果再次按下应该停止白方的计时器,然后启动黑方的倒计时
  3. 然后如果再次按下应该停止黑色的计时器,然后启动白色的计时器。
  4. 并重复直到计时器用完并且两人中的一人被宣布为获胜者。

问题: 所以到目前为止我编码的内容,当按下“开关”时,它会启动白色的计时器,如果再次按下会停止白色的计时器,但它不会启动黑色的计时器。我知道这是因为我构建if() 条件的方式,也因为我不知道如何从外部停止计时器。我所做的是对每个白色和黑色使用 - bool checkTimerWcheckTimerB,我在if()条件中检查以取消计时器是基于它的。

代码:

提供者 -

import 'dart:async';

import 'package:flutter/foundation.dart';

class SettingsProvider extends ChangeNotifier {
  int valueW = 0;
  int valueB = 0;

// * with these booleans we will stop the timer.
  bool checkTimerW = true;
  bool checkTimerB = true;


  String timeToDisplayW = ""; // for white
  String timeToDisplayB = ""; // for black

  bool switchT = false;

   // this is called in the settings Modal Bottom Sheet
  void changeValue(int valW, int valB) {
    
   //? Changing the value in seconds
    valueW = valW * 60;
    valueB = valB * 60;

    print(valueW);

    timeToDisplayW = valueW.toString();
    timeToDisplayB = valueB.toString();
    notifyListeners();
  }

  void reset() {
    started = true;
    stopped = true;
    checkTimerW = false;
    checkTimerB = false;
    notifyListeners();
  }

  void toggleSwitch() {
    if (switchT == false) {
      switchT = true;
      print('true');
    } else if (switchT == true) {
      switchT = false;
      print('false');
    }
  }

  void switchTimer() {
    if (switchT == false) {

      // Starts white's timer

      Timer.periodic(
        Duration(seconds: 1),
        (Timer t) {
          if (valueW <= 1 || checkTimerW == false) {
            t.cancel();
            checkTimerW = true;

            // TODO : Black Won

            notifyListeners();
          } else {
            valueW = valueW - 1;
            notifyListeners();
          }

          timeToDisplayW = valueW.toString();
          notifyListeners();
        },
      );

      // stops black's timer

      checkTimerB = false;
      toggleSwitch();
      notifyListeners();

    } else {

      // Starts black's timer

      Timer.periodic(
        Duration(seconds: 1),
        (Timer t) {
          if (valueB <= 1 || checkTimerB == false) {
            t.cancel();
            checkTimerB = true;

            // TODO : White won

            notifyListeners();
          } else {
            valueB = valueB - 1;
            notifyListeners();
          }

          timeToDisplayB = valueB.toString();
          notifyListeners();
        },
      );

      // stops white's timer
      checkTimerW = false;
      toggleSwitch();
      notifyListeners();
      
  }
 }
}

Main.dart -

import 'dart:math';


import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';

import 'controller/countdown_controller.dart';
import 'widgets/blackButton.dart';
import 'widgets/bottom_sheet_design.dart';
import 'widgets/whiteButton.dart';
import 'providers/settings.dart';


void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    SystemChrome.setPreferredOrientations([
      DeviceOrientation.portraitUp,
      DeviceOrientation.portraitDown,
    ]);
    return ChangeNotifierProvider(
      create: (ctx) => SettingsProvider(),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.amber,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
      
  void settings(BuildContext ctx) {
    showModalBottomSheet(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.only(
          topLeft: Radius.circular(10),
          topRight: Radius.circular(10),
        ),
      ),
      context: ctx,
      builder: (_) => BottomSheetDesign(),
    );
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        backgroundColor: Colors.grey[350],
        body: Row(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Expanded(
              flex: 1,
              child: Transform.rotate(
                angle: pi / 1,
                child: GestureDetector(
                  onTap: () {
                    Provider.of<SettingsProvider>(context, listen: false)
                        .switchTimer();
                  },
                  child: Container(
                    width: 80.0,
                    height: 500,
                    child: Center(
                      child: Transform.rotate(
                        angle: pi / 2,
                        child: Text('Switch',
                            style: Theme.of(context).textTheme.bodyText2),
                      ),
                    ),
                    decoration: BoxDecoration(
                      color: Colors.blueGrey,
                    ),
                  ),
                ),
              ),
            ),
            VerticalDivider(),
            Expanded(
              flex: 4,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Row(
                    children: <Widget>[

                      // container that displays black's timer
                      BlackButton(),

                      Expanded(
                        child: Transform.rotate(
                          angle: pi / 2,
                          child: RaisedButton(
                            onPressed: () {
                              settings(context);
                            },
                            color: Colors.blue[300],
                            child: Text('Settings'),
                          ),
                        ),
                      ),
                    ],
                  ),
                  SizedBox(
                    height: 20,
                  ),
                  Row(
                    children: <Widget>[

                      // container that displays white's timer
                      WhiteButton(),

                      Expanded(
                        child: Transform.rotate(
                          angle: pi / 2,
                          child: RaisedButton(
                            onPressed: () {
                              Provider.of<SettingsProvider>(context, listen: false).reset();
                            },
                            color: Colors.red[600],
                            child: Text('Reset'),
                          ),
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}
4

2 回答 2

1

我过去已经对此进行了编码。它可能会帮助你。

class DoubleTimer extends StatefulWidget {
  @override
  _DoubleTimerState createState() => _DoubleTimerState();
}

class _DoubleTimerState extends State<DoubleTimer> {
  int timeToGoA = 50000;
  int timeToGoB = 50000;

  int state = 0; //0: waiting, 1: counting A, 2: counting B

  DateTime timeStamp;

  _DoubleTimerState() {
    print("init");
  }

  @override
  Widget build(BuildContext context) {
    print(
        "${DateTime.now().compareTo(DateTime.now().add(Duration(seconds: 1)))}");
    return Row(
      children: <Widget>[
        if (state == 1)
          ToTime(timeStamp.add(Duration(milliseconds: timeToGoA))),
        FlatButton(
          onPressed: () {
            setState(() {
              switch (state) {
                case 0:
                  state = 1;
                  timeStamp = DateTime.now();
                  print("Running A");
                  break;
                case 1:
                  state = -1;
                  timeToGoA -=
                      DateTime.now().difference(timeStamp).inMilliseconds;
                  timeStamp = DateTime.now();
                  print("A: $timeToGoA\nRunning B");
                  break;
                case -1:
                  state = 1;
                  timeToGoB -=
                      DateTime.now().difference(timeStamp).inMilliseconds;
                  timeStamp = DateTime.now();
                  print("B: $timeToGoB\nRunning A");
                  break;
              }
            });
          },
          child: Text("switch"),
        ),
        if (state == -1)
          ToTime(timeStamp.add(Duration(milliseconds: timeToGoB))),
      ],
    );
  }
}

class ToTime extends StatelessWidget {
  final DateTime timeStamp;

  const ToTime(this.timeStamp, {Key key}) : super(key: key);

  static final Map<String, int> _times = <String, int>{
    'y': -const Duration(days: 365).inMilliseconds,
    'm': -const Duration(days: 30).inMilliseconds,
    'w': -const Duration(days: 7).inMilliseconds,
    'd': -const Duration(days: 1).inMilliseconds,
    'h': -const Duration(hours: 1).inMilliseconds,
    '\'': -const Duration(minutes: 1).inMilliseconds,
    '"': -const Duration(seconds: 1).inMilliseconds,
    "ms": -1,
  };

  Stream<String> get relativeStream async* {
    while (true) {
      int duration = DateTime.now().difference(timeStamp).inMilliseconds;
      String res = '';
      int level = 0;
      int levelSize;
      for (MapEntry<String, int> time in _times.entries) {
        int timeDelta = (duration / time.value).floor();
        if (timeDelta > 0) {
          levelSize = time.value;
          res += '$timeDelta${time.key} ';
          duration -= time.value * timeDelta;
          level++;
        }
        if (level == 2) {
          break;
        }
      }
      levelSize ??= _times.values.reduce(min);
      if (level > 0 && level < 2) {
        List<int> _tempList =
            _times.values.where((element) => (element < levelSize)).toList();

        if (_tempList.isNotEmpty) levelSize = _tempList.reduce(max);
      }
      if (res.isEmpty) {
        yield 'now';
      } else {
        res.substring(0, res.length - 2);
        yield res;
      }
//      print('levelsize $levelSize sleep ${levelSize - duration}ms');
      await Future.delayed(Duration(milliseconds: levelSize - duration));
    }
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<String>(
        stream: relativeStream,
        builder: (context, snapshot) {
          return Text(snapshot.data ?? '??');
        });
  }
}
于 2020-06-26T16:16:25.897 回答
1

我在没有提供者的情况下对其进行了编码(仅使用 ValueNotifier)只是为了向您展示逻辑

enum Player{White, Black}

class MyTimer extends ValueNotifier<int>{
  Player _turn; //White starts
  int _minutes;
  int _whiteTime;
  int _blackTime;
  
  MyTimer(int time) : 
       _minutes = time * 60,
       _whiteTime = time * 60,
       _blackTime = time * 60,
       _turn = Player.White, //White starts
       super(time * 60 * 2);
  
  bool get _isWhiteTurn => Player.White == _turn;
  
  String get timeLeft{
    if(value != 0){
      //int time = _isWhiteTurn ? _whiteTime : _blackTime; //use this instead of playerTime if you want to display the time in seconds
      Duration left = Duration(seconds: _isWhiteTurn ? _whiteTime : _blackTime);
      String playerTime = left.toString();
      playerTime = playerTime.substring(0, playerTime.lastIndexOf('.'));
      return '${describeEnum(_turn)} turn time left : $playerTime';
    } 
    else{
      return '${describeEnum(_turn)} wins!'; //We have a winner
    }
  }
  
  void switchPlayer() => _turn = _isWhiteTurn ? Player.Black : Player.White;
  void reset([int time]){
    if(time != null) _minutes = time * 60; //if you want to start with a different  value
    _turn = Player.White; //White starts
    _whiteTime = _minutes; //reset time
    _blackTime = _minutes; //reset time
    value = 2*_minutes; //reset time
    //twice as long because it counts the whole time of the match (the time of the 2 players)
  }
  void start(){
    _initilizeTimer();
  }
  void _initilizeTimer(){
    Timer.periodic(
      Duration(seconds: 1),
      (Timer t) {
        if(_whiteTime == 0 || _blackTime == 0){
          t.cancel();
          switchPlayer(); //the time of one player ends, so it switch to the winner player
          value = 0; //end the game
        }
        else{
          _isWhiteTurn ? --_whiteTime : --_blackTime;
          --value;
        }
      },
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final MyTimer clock = MyTimer(1);
  
  @override
  void initState(){
    super.initState();
    clock.start();
  }
  
  @override
  void dispose(){
    clock.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        backgroundColor: Colors.grey[350],
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              ValueListenableBuilder<int>(
                valueListenable: clock,
                 builder: (context, unit, _) =>
                   Text(clock.timeLeft ,style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500))
              ),
              RaisedButton(
                child: Text('Switch'),
                onPressed: () => clock.switchPlayer(),
              )
            ],
          ),
        )
      ),
    );
  }
}

这个想法是相同的,但我想向您展示的是,您可以只使用一个计时器来执行整个逻辑,并且在两分钟之间更改一些枚举值(白色和黑色)。

该按钮更改玩家的轮次(switchPlayer 方法),在计时器内,您会看到根据玩家的轮次,它会减少时间_isWhiteTurn ? --_whiteTime : --_blackTime;。作为 ValueNotifier,它仅在值更改时更新,但您可以将 Provider 与 ChangeNotifier 一起使用并在需要时更新(这更好,因为在我的示例中更改播放器时,我仍然必须等待第二个结束,以便计时器更新文本)。

您可以尝试使用枚举更改类似的内容以简化计时器逻辑

bool get _isWhiteTurn => Player.White == _turn;

void startMatch() {
      Timer.periodic(
        Duration(seconds: 1),
        (Timer t) {
          if (valueW == 0 || valueB == 0) {
            t.cancel();
            if(valueW == 0) checkTimerB = true;
            else checkTimerW = true
            //it won the one whose time didn't end
          } else {
            _isWhiteTurn ? --valueW : --valueB;
          }

          timeToDisplayW = valueW.toString();
          timeToDisplayB = valueB.toString();
          //only one of them will change
          notifyListeners();
        },
      );
}

void switchTimer(){
  _turn = _isWhiteTurn ? Player.Black : Player.White;
  notifyListeners();
}

这样,您只有一个计时器,整个比赛将在其中一个计时器变为 0 时取消(或者如果有人失控,但我猜这是其他提供者中的其他逻辑)

更新

您可以将 timeLeft getter 更改为这样的东西

String get timeLeft{
    if(value != 0){
      //int time = _isWhiteTurn ? _whiteTime : _blackTime; //use this instead of playerTime if you want to display the time in seconds
      Duration white = Duration(seconds: _whiteTime);
      Duration black = Duration(seconds: _blackTime);
      String whiteTime = white.toString();
      String blackTime = black.toString();
      whiteTime = whiteTime.substring(0, whiteTime.lastIndexOf('.'));
      blackTime = blackTime.substring(0, blackTime.lastIndexOf('.'));
      return '''
                ${describeEnum(Player.White)} time left : $whiteTime
                ${describeEnum(Player.Black)} time left : $blackTime
      ''';
    } 
    else{
      return '${describeEnum(_turn)} wins!'; //We have a winner
    }
}

这样,它将返回一个包含两个时间的字符串,并且只有玩家的计时器会每秒改变。但是正如我所说,尝试使用 ChangeNotifierProvider 这个逻辑,它也应该可以工作,你可以在你的小部件树的不同部分使用它

在此处输入图像描述

于 2020-06-26T17:55:04.377 回答