首页 > 解决方案 > 试图制定一个公式来计算 2 个 dateTimes 之间的时间以在 Flutter 中激活 Dark Theme

问题描述

这不是关于 Flutter 的问题,而是关于 Dart 和数学的问题。

当用户打开自动黑暗主题开关或返回应用程序时,我试图自己制作我的应用程序主题。

我已经为用户设置了更改自动黑暗主题的初始时间结束时间的选项,这为我们提供了DateTime()变量,其中包含年、月、日、小时、分钟、秒等数据。 ( 24 小时制的小时数)

现在我有这个公式:

if ((now.hour >= _startTime.hour && now.minute >= _startTime.minute) || (now.hour <= _endTime.hour && now.minute <= _endTime.minute)) {
      setTheme('dark');
} else {
      setTheme('light');
}

我认为它工作得很好,但正如你在这张图片和设置中看到的那样。它没有正常工作...... 在此处输入图像描述

这些是变量:

_startTime = 1:00 AM = 1
now = 10:40PM = 22:40
_endTime = 8:00 AM = 8

通过使用我的公式:((now.hour >= _startTime.hour && now.minute >= _startTime.minute) || (now.hour <= _endTime.hour && now.minute <= _endTime.minute))这会创建一个True和一个False,它们等于True并激活黑暗主题。

我怎样才能完善这个公式?这似乎很容易做到,但实际上非常混乱。

编辑:对于那些喜欢处理数字的人

让我们从选择器(允许用户输入日期或时间的小部件)开始

class HourPickers extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    CustomAppThemeProvider themeProvider =
        Provider.of<CustomAppThemeProvider>(context, listen: false);
    DateTime startTime = themeProvider.getStartTime;
    DateTime endTime = themeProvider.getEndTime;
    print('from provider:\n$startTime & $endTime');
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Row(
            crossAxisAlignment: CrossAxisAlignment.center,
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              Switch(
                value: themeProvider.getIsAutoThemeSwitchOn,
                activeColor: Colors.green,
                onChanged: (boolean) {
                  themeProvider.setIsAutoThemeSwitchOn(boolean);
                },
              ),
              Text(
                'Tema Oscuro Automático',
                style: Theme.of(context).textTheme.subtitle,
              ),
            ],
          ),
          Text(
            'Inicio',
            style: Theme.of(context).textTheme.subtitle,
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 5),
            child: SizedBox(
              height: 100,
              child: Card(
                child: CupertinoDatePicker(
                  mode: CupertinoDatePickerMode.time,
                  initialDateTime: DateTime(startTime.year, startTime.month,
                      startTime.day, startTime.hour, startTime.minute),
                  onDateTimeChanged: (DateTime pickedTime) {
                    Provider.of<CustomAppThemeProvider>(context, listen: false)
                        .setStartTime(pickedTime);
                  },
                  use24hFormat: false,
                  backgroundColor: Theme.of(context).colorScheme.primary,
                ),
              ),
            ),
          ),
          Text(
            'Final',
            style: Theme.of(context).textTheme.subtitle,
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 5),
            child: SizedBox(
              height: 100,
              child: Card(
                child: CupertinoDatePicker(
                  mode: CupertinoDatePickerMode.time,
                  initialDateTime: DateTime(endTime.year, endTime.month,
                      endTime.day, endTime.hour, endTime.minute),
                  onDateTimeChanged: (DateTime pickedTime) {
                    Provider.of<CustomAppThemeProvider>(context, listen: false)
                        .setEndTime(pickedTime);
                  },
                  use24hFormat: false,
                  backgroundColor: Theme.of(context).colorScheme.primary,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

这就是提供者去的地方(我刚刚取出了主题部分)

bool get getIsAutoThemeSwitchOn => _isAutoThemeSwitchOn;

  Future<bool> getPrefIsAutoThemeSwitchOn() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    bool _prefAutoTheme = prefs.getBool('AutoTheme') ?? false;
    _isAutoThemeSwitchOn = _prefAutoTheme;
    return _prefAutoTheme;
  }

  Future<void> setIsAutoThemeSwitchOn(boolean) async {
    _isAutoThemeSwitchOn = boolean;
    // print(_isAutoThemeSwitchOn);
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setBool('AutoTheme', boolean);
    if (boolean == true) {
      autoThemeSetter();
    } else {
      notifyListeners();
    }
  }

  void autoThemeSetter() {
    // only if switch is ON
    DateTime now = DateTime.now();

    if (_startTime.isAfter(_endTime)) _endTime = _endTime.add(Duration(days: 1));

    print('start: $_startTime');
    print('now: $now');
    print('end: $_endTime');
    print('now >= start? ${now.isAfter(_startTime)}');
    print('now <= end? ${now.isBefore(_endTime)}');

    if (now.isAfter(_startTime) && now.isBefore(_endTime)) {
      // print('auto dark theme');
      setTheme('dark');
    } else {
      // print('auto light theme');
      setTheme('light');
    }
    // had add notify even if switch != true because the switch value needs to update
    notifyListeners();
  }

  getPrefTimes() async {
    await getPrefsStartTime();
    await getPrefsEndTime();
  }

  getPrefsStartTime() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    if (prefs.getInt('startTime') != null) {
      DateTime now = DateTime.now();
      _startTime =
          DateTime.fromMillisecondsSinceEpoch(prefs.getInt('startTime'));

      print('start pref: $_startTime');

      // set the day equal to now.day
      if (now.day != _startTime.day) {
        _startTime = DateTime(now.year, now.month, now.day, _startTime.hour, _startTime.minute);
      }
      // sub -1 day if needed
      if (_startTime.isAfter(now)) _startTime = _startTime.subtract(Duration(days: 1));

      // update SharedPref because why not
      // setStartTime(_startTime);
    }
  }

  getPrefsEndTime() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    if (prefs.getInt('endTime') != null) {
      DateTime now = DateTime.now();
      _endTime = DateTime.fromMillisecondsSinceEpoch(prefs.getInt('endTime'));

      print('end pref: $_endTime');

      // set the day equal to now.day
      if (now.day != _endTime.day) {
        _endTime = DateTime(now.year, now.month, now.day, _endTime.hour, _endTime.minute);
      }
      // add +1 day if needed
      if (_startTime.isAfter(_endTime)) _endTime = _endTime.add(Duration(days: 1));

      // update SharedPref because why not
      // setStartTime(_endTime);
    }
  }

  get getStartTime => _startTime;
  get getEndTime => _endTime;

  Future<void> setStartTime(DateTime pickedTime) async {
    // print(pickedTime);
    SharedPreferences prefs = await SharedPreferences.getInstance();
    DateTime now = DateTime.now();
    _startTime = pickedTime;
    // sub -1 day if needed
      if (_startTime.isAfter(now)) _startTime = _startTime.subtract(Duration(days: 1));
    int epoch = _startTime.millisecondsSinceEpoch;
    prefs.setInt('startTime', epoch);
    // print(prefs.getInt('startTime'));
  }

  Future<void> setEndTime(DateTime pickedTime) async {
    // print(pickedTime);
    SharedPreferences prefs = await SharedPreferences.getInstance();
    DateTime now = DateTime.now();
    _endTime = pickedTime;
    // sub -1 day if needed
      if (_startTime.isAfter(now)) _startTime = _startTime.subtract(Duration(days: 1));
    int epoch = _endTime.millisecondsSinceEpoch;
    prefs.setInt('endTime', epoch);
  }
}

您已经可以看到这并不像看起来那么容易,只有自动主题切换器的背景有很多。

顺便说一句,每次应用程序从后台恢复时都会调用 autoThemeSetter()。

标签: fluttermathdart

解决方案


您使用的公式存在一些逻辑缺陷。主要是,它不检查 now 是否在 _startTime 和 _endTime 之间(OR 应该是 AND ......但仍然存在问题)

只是为了解释起见,您使用的逻辑是“如果当前时间在开始时间之后或者如果当前时间在结束时间之前,则使主题变暗”

假设当前时间是上午 9 点,开始时间是上午 1 点,结束时间是上午 8 点。这会将主题设置为深色,因为您使用了 OR,而上午 9 点是在凌晨 1 点之后。因此,它通过了,因为 OR 不是关于它在 endTime 之后的事实。因此,您需要使用 && 来检查两者,因为您想检查当前时间是否在 _startTime 之后和 _endTime 之前。

不幸的是,你不能把它变成一个 && 并称之为一天,因为小时和分钟是分开的。所以,假设时间是早上 6 点 01 分......并且开始和结束与之前相同。在这种情况下,第一部分为真(当前时间分钟和小时大于 _startTime 分钟和小时,但它会将主题设置为 LIGHT,因为当前分钟(01)不小于 _endTime 分钟,尽管它很明显在范围内。

因此,一种选择是将所有内容转换为分钟,如下所示:

  if ((now.hour * 60 + now.minute) >=
          (_startTime.hour * 60 + _startTime.minute) &&
      ((now.hour * 60 + now.minute) <=
          (_endTime.hour * 60 + _endTime.minute))) {
    print('dark');
  } else {
    print('light');
  }

但这有不检查日期的缺点。例如,如果您希望开始时间为 2300 并且停止时间为 0800,则它将不起作用。

另一种(更好的)方法是使用 .isBefore() 和 .isAfter() 方法。但是,您需要确保他们使用正确的日期,因为您使用的是 DateTime() 对象。这可能看起来像:

    var stringDate = DateTime.now().year.toString() + "-" +    DateTime.now().month.toString().padLeft(2, '0') + "-" + DateTime.now().day.toString().padLeft(2, '0'); 
//I'm assuming that all dates are the same, but you should use the correct dates.

      print(stringDate);
      var now_2 = DateTime.now();

      var _startTime_2 = DateTime.parse(stringDate + " 01:00:00Z");
      var _endTime_2 = DateTime.parse(stringDate+" 08:00:00Z");

      print("now_2="+now_2.toString());
      print("_startTime_2="+_startTime_2.toString());
      print("_endTime_2="+_endTime_2.toString());

        print("inBefore method");
      print("---------------------------------");
      if (_startTime_2.isBefore(now_2) && now_2.isBefore(_endTime_2)) {
        print('dark');
      } else {
        print('light');
      }

我制作了一个 dartpad 文件,以便您可以在此处使用代码: https ://dartpad.dev/1f978458eabcaf588c872fa4f6bfdaf0


推荐阅读