feat: Phase 2 — upgrade deps to null-safe, replace deprecated packages

- pubspec: bump all maintained deps to null-safe latest; flutter pub get resolves
- Remove 12 packages (0-ref or no null-safe): splashscreen, flappy_search_bar,
  searchable_dropdown, share, platform_detect, flutter_inappwebview, countdown,
  gender_selection, flutter_dash, smooth_star_rating, hovering, ffi
- Add flutter_rating_bar
- Keep discontinued-but-null-safe for now (Phase 5): pull_to_refresh, hive, catcher
- New null-safe custom components (API-compatible, callers change only imports):
  utils/countdown.dart, widgets/general/{hover_widget,gender_selection,
  smooth_star_rating,search_bar}.dart
- main.dart: drop dead SplashScreen _buildBody + _to field
- 23 consuming files: swap deprecated imports to local components

Null-safety migration + upgraded-API adaptation = Phase 3.
This commit is contained in:
2026-07-24 03:10:12 +08:00
parent 7ffcc848d0
commit 1a578c925d
37 changed files with 1252 additions and 574 deletions

29
lib/utils/countdown.dart Normal file
View File

@@ -0,0 +1,29 @@
import 'dart:async';
/// 自定义实现,替代已停维的 `countdown` 包的 [CountDown]。
///
/// 用法与原包兼容:
/// ```dart
/// var listener = CountDown(Duration(seconds: 90)).stream.listen(null);
/// listener.onData((Duration d) { ... d.inSeconds ... });
/// listener.onDone(() { ... });
/// ```
///
/// `stream` 每秒发出剩余 [Duration](从满时长倒数到 0到达 0 后关闭。
class CountDown {
CountDown(this.duration) : assert(!duration.isNegative);
final Duration duration;
Stream<Duration> get stream => _countdownStream();
Stream<Duration> _countdownStream() async* {
int remaining = duration.inSeconds;
while (remaining > 0) {
yield Duration(seconds: remaining);
await Future<void>.delayed(const Duration(seconds: 1));
remaining--;
}
yield Duration.zero;
}
}