周睿 há 2 anos atrás
pai
commit
419df0c553

+ 7 - 0
app_business/lib/pages.dart

@@ -1,3 +1,4 @@
+import 'package:app_business/view/home/home.dart';
 import 'package:get/get.dart';
 import 'package:track_common/view.dart';
 
@@ -16,5 +17,11 @@ final getPages = [
       page: () => const LoginView(),
       binding: BindingsBuilder(() {
         Get.put<LoginController>(LoginControllerImp());
+      })),
+  GetPage(
+      name: HomeView.name,
+      page: () => const HomeView(),
+      binding: BindingsBuilder(() {
+        Get.put<HomeController>(HomeControllerImpl());
       }))
 ];

+ 8 - 2
app_business/lib/service/all_init.dart

@@ -1,15 +1,21 @@
 import 'package:app_business/service/api.dart';
 import 'package:app_business/service/app.dart';
+import 'package:track_common/track_common.dart';
 
 import 'abase.dart';
 
 Future<void> allInit() async {
+  final app = Get.put(AppService(), permanent: true);
+  final api = Get.put(ApiService(), permanent: true);
+
   final services = <IService>[
-    Get.put<AppService>(AppService()),
-    Get.put<ApiService>(ApiService()),
+    app,
+    api,
   ];
 
   for (var service in services) {
     await service.init();
   }
+
+  info('初始化完成');
 }

+ 12 - 0
app_business/lib/service/api.dart

@@ -7,6 +7,7 @@ import 'package:app_business/generated/track_offical.pbgrpc.dart' as pb;
 import 'package:app_business/service/app.dart';
 import 'package:grpc/grpc.dart';
 import 'package:track_common/track_common.dart';
+import 'package:track_common/view/home/map/map_page.dart';
 
 import 'abase.dart';
 
@@ -99,6 +100,17 @@ class ApiService extends IService {
     return r.second.seconds;
   }
 
+  Future<List<MapInfo>> getMapList(int limit, int offset) async {
+    final r = await stub.toMapListV2(pb.MapListRequestV2()
+      ..limit = limit
+      ..offset = offset);
+
+    return r.list
+        .map((e) => MapInfo(e.mapId, e.name, e.distance.meter, e.image.url,
+            e.description, e.mapScaleNumber, e.image.md5))
+        .toList();
+  }
+
   Future<BinReader> getBinReaderByMd5(Uint8List md5) async {
     final stream =
         stub.toGetBinaryByMd5(pb.ToGetBinaryByMd5Request()..md5 = md5);

+ 10 - 0
app_business/lib/view/all_init.dart

@@ -1,4 +1,7 @@
+import 'package:app_business/service/abase.dart';
 import 'package:app_business/service/all_init.dart' as a;
+import 'package:app_business/service/api.dart';
+import 'package:track_common/track_common.dart';
 import 'package:track_common/view.dart';
 
 class InitControllerImp extends InitController {
@@ -9,6 +12,13 @@ class InitControllerImp extends InitController {
 
   @override
   Future<bool> isNeedLogin() async {
+    try {
+      await Get.find<ApiService>().getMapList(1, 0);
+      return false;
+    } catch (e) {
+      debug(e);
+    }
+
     return true;
   }
 }

+ 20 - 0
app_business/lib/view/home/home.dart

@@ -0,0 +1,20 @@
+import 'package:app_business/view/home/maplist.dart';
+import 'package:get/get.dart';
+import 'package:track_common/view.dart';
+import 'package:track_common/view/home/map/map_page.dart';
+
+class HomeControllerImpl extends HomeController {
+  @override
+  int get initTabIdx => 0;
+
+  @override
+  List<HomeTab> get tabs => [
+        HomeTab(
+            '地图',
+            () => GetBuilder<MapPageController>(
+                init: MapPageControllerImpl(),
+                builder: (c) {
+                  return const MapPage();
+                })),
+      ];
+}

+ 22 - 0
app_business/lib/view/home/maplist.dart

@@ -0,0 +1,22 @@
+import 'package:app_business/service/abase.dart';
+import 'package:app_business/service/api.dart';
+import 'package:track_common/view.dart';
+import 'package:track_common/view/home/map/map_page.dart';
+
+class MapPageControllerImpl extends MapPageController {
+  ApiService get _api => Get.find();
+  final HomeController home = Get.find();
+
+  @override
+  Future<List<MapInfo>> getMapList(int limit, int offset) {
+    return _api.getMapList(limit, offset);
+  }
+
+  @override
+  set selectedMap(MapInfo map) {
+    home.selectMapName.value = map.name;
+  }
+
+  @override
+  int get selectedMapId => 0;
+}

+ 2 - 0
libs/track_common/lib/view.dart

@@ -1,3 +1,5 @@
+export 'view/home/home_controller.dart';
+export 'view/home/home_view.dart';
 export 'view/init_view.dart';
 export 'view/login/login_controller.dart';
 export 'view/login/login_view.dart';

+ 63 - 0
libs/track_common/lib/view/home/app_bar.dart

@@ -0,0 +1,63 @@
+import 'package:track_common/widget/prelude.dart';
+
+import 'home_controller.dart';
+
+class HomeAppBar extends GetView<HomeController>
+    implements PreferredSizeWidget {
+  const HomeAppBar({super.key, required this.tab});
+
+  final Widget tab;
+
+  @override
+  Widget build(BuildContext context) {
+    final statusBarHeight = MediaQuery.of(context).viewPadding.top;
+
+    return SizedBox.expand(
+        child: Container(
+      padding: EdgeInsets.only(top: statusBarHeight),
+      decoration: const BoxDecoration(
+          image: DecorationImage(
+              image: AssetImage(Assets.imagesBkCommonPage, package: package),
+              fit: BoxFit.fitWidth),
+          boxShadow: [
+            BoxShadow(color: Color(0x33000000), spreadRadius: 4, blurRadius: 4)
+          ]),
+      child: Row(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.center,
+        children: [
+          SizedBox(width: 660, height: double.infinity, child: tab),
+          Expanded(
+              child: Obx(() => controller.selectMapName.value.isEmpty
+                  ? const SizedBox()
+                  : Row(children: [
+                      Container(
+                        width: 10,
+                        height: 34,
+                        margin: const EdgeInsets.only(right: 12),
+                        decoration: BoxDecoration(
+                            color: Colors.blue,
+                            borderRadius: BorderRadius.circular(5)),
+                      ),
+                      Text(controller.selectMapName.value,
+                          style: const TextStyle(
+                              color: Colors.white, fontSize: 22))
+                    ]))),
+          TextButton(
+              onPressed: () {},
+              child: const Row(
+                crossAxisAlignment: CrossAxisAlignment.center,
+                children: [
+                  Icon(Icons.radio, color: Colors.white),
+                  Text(' 广播',
+                      style: TextStyle(color: Colors.white, fontSize: 20))
+                ],
+              ))
+        ],
+      ),
+    ));
+  }
+
+  @override
+  Size get preferredSize => const Size.fromHeight(kToolbarHeight);
+}

+ 31 - 0
libs/track_common/lib/view/home/home_controller.dart

@@ -0,0 +1,31 @@
+import 'package:common_pub/ui/history_detail/trace_bar.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+
+abstract class HomeController extends GetxController
+    with GetTickerProviderStateMixin {
+  final selectMapName = ''.obs;
+  late final TabController tabController;
+  late final traceController = TraceBarController();
+  int get initTabIdx => 1;
+  List<HomeTab> get tabs;
+
+  @override
+  void onInit() {
+    super.onInit();
+    tabController = TabController(
+        length: tabs.length, initialIndex: initTabIdx, vsync: this);
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+    tabController.dispose();
+  }
+}
+
+class HomeTab {
+  HomeTab(this.title, this.builder);
+  final String title;
+  final Widget Function() builder;
+}

+ 77 - 0
libs/track_common/lib/view/home/home_view.dart

@@ -0,0 +1,77 @@
+import 'package:track_common/widget/prelude.dart';
+
+import 'app_bar.dart';
+import 'home_controller.dart';
+
+class HomeView extends GetView<HomeController> {
+  const HomeView({super.key});
+  static const name = '/HomeView';
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: HomeAppBar(
+        tab: TabBar(
+            controller: controller.tabController,
+            tabs: controller.tabs.map((e) => Tab(text: e.title)).toList(),
+            labelColor: Colors.white,
+            unselectedLabelColor: Colors.white,
+            indicatorSize: TabBarIndicatorSize.tab,
+            indicatorPadding: const EdgeInsets.only(bottom: 0),
+            indicator: CustomTabIndicator(),
+            dividerColor: Colors.transparent),
+      ),
+      body: Container(
+          decoration: const BoxDecoration(
+              image: DecorationImage(
+                  image:
+                      AssetImage(Assets.imagesBkCommonPage, package: package),
+                  fit: BoxFit.fill)),
+          child: TabBarView(
+            controller: controller.tabController,
+            physics: const NeverScrollableScrollPhysics(),
+            children: controller.tabs.map((e) => e.builder()).toList(),
+          )),
+    );
+  }
+}
+
+// final _tabElems = [
+// _TabElem(title: '设置', child: const SizedBox()),
+// _TabElem(title: '地图', child: const MapPage()),
+// _TabElem(title: '场控', child: const FieldControlPage()),
+// _TabElem(title: '用户管理', child: const UserManagePage()),
+// _TabElem(title: '个人排名', child: PersonalRankPage()),
+// _TabElem(title: '分组排名', child: const SizedBox()),
+// _TabElem(title: '数据详情', child: const DataDetailPage()),
+// ];
+
+class CustomTabIndicator extends Decoration {
+  @override
+  BoxPainter createBoxPainter([VoidCallback? onChanged]) {
+    return _CustomPainter(this, onChanged!);
+  }
+}
+
+class _CustomPainter extends BoxPainter {
+  final CustomTabIndicator decoration;
+
+  _CustomPainter(this.decoration, VoidCallback onChanged) : super(onChanged);
+
+  @override
+  void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
+    final size = configuration.size;
+    assert(size != null);
+    final Paint paint = Paint();
+    paint.color = const Color(0xff003656);
+    paint.style = PaintingStyle.fill;
+    final path = Path();
+    final rect = offset & size!;
+    path.moveTo(rect.center.dx - 12, rect.bottom);
+    path.lineTo(rect.center.dx + 12, rect.bottom);
+    path.lineTo(rect.center.dx, rect.height - 12);
+    path.close();
+
+    canvas.drawPath(path, paint);
+  }
+}

+ 216 - 0
libs/track_common/lib/view/home/map/map_page.dart

@@ -0,0 +1,216 @@
+import 'package:common_pub/model/distance.dart';
+import 'package:common_pub/model/position.dart' as m;
+import 'package:track_common/widget/prelude.dart';
+
+import '../../../utils.dart';
+import '../../../widget.dart';
+import '../../../widget/app_net_image.dart';
+
+export 'package:common_pub/model/distance.dart';
+
+class MapInfo {
+  const MapInfo(this.id, this.name, this.distance, this.url, this.description,
+      this.mapScaleNumber, this.md5);
+  final int id;
+  final String name;
+  final Distance? distance;
+  final String url;
+  final String description;
+  final int mapScaleNumber;
+  final List<int> md5;
+}
+
+abstract class MapPageController extends GetxController {
+  @override
+  void onInit() {
+    super.onInit();
+    mapGetMore();
+  }
+
+  Future<List<MapInfo>> getMapList(int limit, int offset);
+
+  int get selectedMapId;
+
+  set selectedMap(MapInfo map);
+
+  Future<void> mapGetMore() async {
+    if (isMapGetMoreLoading.value) {
+      return;
+    }
+    isMapGetMoreLoading.value = true;
+    await tryApi(() async {
+      final r = await getMapList(20, mapList.length);
+      mapList.addAll(r);
+      return;
+    }, onFinally: () {
+      isMapGetMoreLoading.value = false;
+    });
+
+    mapListScrollController.addListener(() {
+      if (mapListScrollController.position.pixels ==
+          mapListScrollController.position.maxScrollExtent) {
+        //达到最大滚动位置
+        mapGetMore();
+      }
+    });
+  }
+
+  final mapList = <MapInfo>[].obs;
+  final isMapGetMoreLoading = false.obs;
+  final mapListScrollController = ScrollController();
+  final Rx<m.Position?> position = Rx(null);
+}
+
+class MapPage extends GetView<MapPageController> {
+  const MapPage({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      width: double.infinity,
+      height: double.infinity,
+      margin: const EdgeInsets.all(20),
+      padding: const EdgeInsets.fromLTRB(58, 28, 58, 28),
+      decoration: BoxDecoration(
+          color: Colors.white, borderRadius: BorderRadius.circular(16)),
+      child: Column(
+        children: [
+          const Row(
+            children: [
+              TitlePoint(),
+              Text(' 地图列表'),
+            ],
+          ),
+          Expanded(
+              child: Obx(() => GridView.builder(
+                  itemCount: controller.mapList.length,
+                  controller: controller.mapListScrollController,
+                  scrollDirection: Axis.horizontal,
+                  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
+                      //设置列数
+                      crossAxisCount: 2,
+                      //设置横向间距
+                      crossAxisSpacing: 10,
+                      //设置主轴间距
+                      mainAxisSpacing: 10,
+                      childAspectRatio: 1.3),
+                  itemBuilder: (context, i) {
+                    return Obx(() {
+                      final data = controller.mapList[i];
+                      final s = controller.selectedMapId == data.id;
+                      return GalleryCardWidget(
+                          data: data,
+                          position: controller.position.value,
+                          isSelected: s);
+                    });
+                  })))
+        ],
+      ),
+    );
+  }
+}
+
+class GalleryCardWidget extends GetView<MapPageController> {
+  final MapInfo data;
+  final m.Position? position;
+  final bool isSelected;
+
+  const GalleryCardWidget(
+      {super.key,
+      required this.data,
+      required this.position,
+      required this.isSelected});
+
+  void onTap(MapInfo mapInfo) {
+    controller.selectedMap = mapInfo;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    var distance = '--';
+    if (data.distance != null) {
+      distance = data.distance!.toString();
+    }
+
+    return GestureDetector(
+      onTap: () => onTap(data),
+      child: Card(
+        color: Colors.white,
+        shadowColor: isSelected ? Colors.red : null,
+        surfaceTintColor: Colors.white,
+        shape: const RoundedRectangleBorder(
+            borderRadius: BorderRadius.all(Radius.circular(5.44))),
+        clipBehavior: Clip.antiAlias,
+        elevation: isSelected ? 8 : 4,
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            AspectRatio(
+                aspectRatio: 1.1,
+                child: AppNetImage(
+                    netImage: NetImage(url: data.url, md5: data.md5),
+                    fit: BoxFit.fitHeight)),
+            Expanded(
+                child: Padding(
+                    padding: const EdgeInsets.all(6),
+                    child: Column(
+                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+                      crossAxisAlignment: CrossAxisAlignment.start,
+                      children: [
+                        Text(
+                          data.name,
+                          style: const TextStyle(
+                            fontSize: 13.5,
+                            fontWeight: FontWeight.w500,
+                          ),
+                          textAlign: TextAlign.start,
+                          maxLines: 1,
+                          overflow: TextOverflow.ellipsis,
+                        ),
+                        Text(
+                          data.description,
+                          style: const TextStyle(
+                            fontSize: 10,
+                            color: Color(0xffc6c6c6),
+                          ),
+                          textAlign: TextAlign.start,
+                          maxLines: 1,
+                          overflow: TextOverflow.ellipsis,
+                        ),
+                        const Spacer(),
+                        DefaultTextStyle(
+                            style: const TextStyle(
+                                color: Colors.black, fontSize: 10),
+                            child: Row(
+                              crossAxisAlignment: CrossAxisAlignment.end,
+                              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                              children: [
+                                Image.asset(Assets.imagesIcMapScale,
+                                    package: package, height: 9.6),
+                                Text(' 1:${data.mapScaleNumber}'),
+                                const Spacer(),
+                                Image.asset(Assets.imagesIcLocation,
+                                    package: package, height: 9.6),
+                                Text(' $distance'),
+                              ],
+                            ))
+                      ],
+                    ))),
+          ],
+        ),
+      ),
+    );
+  }
+
+  Widget wImage() {
+    return Stack(
+      children: [
+        SizedBox(
+            height: double.infinity,
+            child: AppNetImage(
+                netImage: NetImage(url: data.url, md5: data.md5),
+                fit: BoxFit.fitHeight)),
+      ],
+    );
+  }
+}

+ 8 - 2
libs/track_common/lib/view/init_view.dart

@@ -1,6 +1,6 @@
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
-import 'package:track_common/view/login/login_view.dart';
+import 'package:track_common/view.dart';
 
 abstract class InitController extends GetxController {
   Future<bool> isNeedLogin();
@@ -18,7 +18,13 @@ abstract class InitController extends GetxController {
   Future<void> init() async {
     await allInit();
     if (await isNeedLogin()) {
-      Get.offNamed(LoginView.name);
+      LoginView.to(
+          canBack: false,
+          thenToPageCall: () {
+            Get.offAllNamed(HomeView.name);
+          });
+    } else {
+      Get.offAllNamed(HomeView.name);
     }
   }
 }

+ 2 - 2
libs/track_common/lib/view/login/login_view.dart

@@ -18,9 +18,9 @@ class LoginView extends GetView<LoginController> {
   }) async {
     Future<dynamic>? r;
     if (canBack) {
-      r = Get.to(LoginView.name, arguments: thenToPageCall);
+      r = Get.toNamed(LoginView.name, arguments: thenToPageCall);
     } else {
-      r = Get.offAll(() => LoginView.name, arguments: thenToPageCall);
+      r = Get.offAllNamed(LoginView.name, arguments: thenToPageCall);
     }
 
     if (r != null) {

+ 97 - 0
libs/track_common/lib/widget/app_net_image.dart

@@ -0,0 +1,97 @@
+import 'package:common_pub/logger.dart';
+import 'package:f_cache/f_cache.dart';
+
+import '../utils.dart';
+import 'prelude.dart';
+
+class AppNetImage extends StatefulWidget {
+  const AppNetImage(
+      {super.key,
+      required this.netImage,
+      this.fit,
+      this.onWidgetOk,
+      this.width,
+      this.height});
+
+  final NetImage netImage;
+  final BoxFit? fit;
+  final VoidCallback? onWidgetOk;
+  final double? width;
+  final double? height;
+
+  @override
+  State<StatefulWidget> createState() {
+    return _AppNetImageState();
+  }
+}
+
+CachedReaderImage cachedProvider(NetImage image) {
+  return CachedReaderImage(image.md5Hex ?? '', image.readerBuilder);
+}
+
+class _AppNetImageState extends State<AppNetImage> {
+  late CachedReaderImage provider;
+
+  @override
+  void initState() {
+    super.initState();
+    final image = widget.netImage;
+    provider = cachedProvider(image);
+  }
+
+  @override
+  void didUpdateWidget(covariant AppNetImage oldWidget) {
+    super.didUpdateWidget(oldWidget);
+    final image = widget.netImage;
+    provider = cachedProvider(image);
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Image(
+      image: provider,
+      loadingBuilder: (BuildContext context, Widget child,
+          ImageChunkEvent? loadingProgress) {
+        if (loadingProgress != null) {
+          var p = 0.0;
+          final all = loadingProgress.expectedTotalBytes ?? 0;
+          if (all > 0) {
+            p = loadingProgress.cumulativeBytesLoaded.toDouble() /
+                all.toDouble();
+          }
+          return ImageLoading(value: p);
+        } else {
+          return child;
+        }
+      },
+      errorBuilder: (ctx, e, trace) {
+        warn('图片加载失败: ${widget.netImage.url}  md5: ${widget.netImage.md5Hex}',
+            e);
+        return Container(
+            color: Colors.white,
+            child: const Center(child: Icon(Icons.broken_image_outlined)));
+      },
+      width: widget.width,
+      height: widget.height,
+      fit: widget.fit,
+    );
+  }
+}
+
+class ImageLoading extends StatelessWidget {
+  final double? value;
+  const ImageLoading({super.key, this.value});
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      decoration: BoxDecoration(color: Colors.grey.withAlpha(20)),
+      child: Center(
+        child: SizedBox(
+            width: 48,
+            height: 48,
+            child: CircularProgressIndicator(value: value)),
+      ),
+    );
+  }
+}

+ 80 - 0
libs/track_common/pubspec.lock

@@ -49,6 +49,30 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.0"
+  cached_network_image:
+    dependency: "direct main"
+    description:
+      name: cached_network_image
+      sha256: f98972704692ba679db144261172a8e20feb145636c617af0eb4022132a6797f
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "3.3.0"
+  cached_network_image_platform_interface:
+    dependency: transitive
+    description:
+      name: cached_network_image_platform_interface
+      sha256: "56aa42a7a01e3c9db8456d9f3f999931f1e05535b5a424271e9a38cabf066613"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "3.0.0"
+  cached_network_image_web:
+    dependency: transitive
+    description:
+      name: cached_network_image_web
+      sha256: "759b9a9f8f6ccbb66c185df805fac107f05730b1dab9c64626d1008cca532257"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.1.0"
   characters:
     dependency: transitive
     description:
@@ -136,6 +160,14 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.0"
+  file:
+    dependency: transitive
+    description:
+      name: file
+      sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "7.0.0"
   fixnum:
     dependency: transitive
     description:
@@ -157,6 +189,14 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_cache_manager:
+    dependency: transitive
+    description:
+      name: flutter_cache_manager
+      sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "3.3.1"
   flutter_lints:
     dependency: "direct dev"
     description:
@@ -303,6 +343,14 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.0.4"
+  octo_image:
+    dependency: transitive
+    description:
+      name: octo_image
+      sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.0.0"
   path:
     dependency: transitive
     description:
@@ -431,6 +479,14 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.2.8"
+  rxdart:
+    dependency: transitive
+    description:
+      name: rxdart
+      sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "0.27.7"
   shelf:
     dependency: transitive
     description:
@@ -476,6 +532,22 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "7.0.0"
+  sqflite:
+    dependency: transitive
+    description:
+      name: sqflite
+      sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.3.0"
+  sqflite_common:
+    dependency: transitive
+    description:
+      name: sqflite_common
+      sha256: bb4738f15b23352822f4c42a531677e5c6f522e079461fd240ead29d8d8a54a6
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.5.0+2"
   stack_trace:
     dependency: transitive
     description:
@@ -500,6 +572,14 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.2.0"
+  synchronized:
+    dependency: transitive
+    description:
+      name: synchronized
+      sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "3.1.0"
   term_glyph:
     dependency: transitive
     description:

+ 1 - 0
libs/track_common/pubspec.yaml

@@ -34,6 +34,7 @@ dependencies:
   http: ^1.1.0
   f_cache: ^3.1.1
   rive: ^0.12.3
+  cached_network_image: ^3.3.0
 
 dev_dependencies:
   flutter_test: