Răsfoiți Sursa

Merge remote-tracking branch 'origin/master'

Resolved conflicts:
- .claude/settings.local.json: merged both permission lists
- ios/Podfile: kept platform :ios, '13.0' specification
- ios/Podfile.lock: used remote version

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
盐城新淘科技有限公司 1 săptămână în urmă
părinte
comite
a68ee2555e
45 a modificat fișierele cu 2498 adăugiri și 138 ștergeri
  1. 12 2
      xinhuaribao/.claude/settings.local.json
  2. 6 9
      xinhuaribao/.metadata
  3. 186 0
      xinhuaribao/CLAUDE.md
  4. 1 1
      xinhuaribao/ios/Podfile
  5. 5 5
      xinhuaribao/ios/Podfile.lock
  6. 3 0
      xinhuaribao/lib/constant/api_const.dart
  7. 3 0
      xinhuaribao/lib/constant/color_res.dart
  8. 101 3
      xinhuaribao/lib/http/http_util.dart
  9. 14 14
      xinhuaribao/lib/main.dart
  10. 13 0
      xinhuaribao/lib/model/new_comment_model.dart
  11. 1 0
      xinhuaribao/lib/provider/common_provider.dart
  12. 301 37
      xinhuaribao/lib/ui/video/comment_input_bar_widget.dart
  13. 266 60
      xinhuaribao/lib/ui/video/comment_item_widget.dart
  14. 4 2
      xinhuaribao/lib/ui/video/comment_page.dart
  15. 2 2
      xinhuaribao/lib/util/share_util.dart
  16. 7 0
      xinhuaribao/macos/.gitignore
  17. 2 0
      xinhuaribao/macos/Flutter/Flutter-Debug.xcconfig
  18. 2 0
      xinhuaribao/macos/Flutter/Flutter-Release.xcconfig
  19. 106 0
      xinhuaribao/macos/Podfile.lock
  20. 801 0
      xinhuaribao/macos/Runner.xcodeproj/project.pbxproj
  21. 8 0
      xinhuaribao/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  22. 99 0
      xinhuaribao/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
  23. 10 0
      xinhuaribao/macos/Runner.xcworkspace/contents.xcworkspacedata
  24. 8 0
      xinhuaribao/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  25. 13 0
      xinhuaribao/macos/Runner/AppDelegate.swift
  26. 68 0
      xinhuaribao/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
  27. BIN
      xinhuaribao/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
  28. BIN
      xinhuaribao/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
  29. BIN
      xinhuaribao/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
  30. BIN
      xinhuaribao/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
  31. BIN
      xinhuaribao/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
  32. BIN
      xinhuaribao/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
  33. BIN
      xinhuaribao/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
  34. 343 0
      xinhuaribao/macos/Runner/Base.lproj/MainMenu.xib
  35. 14 0
      xinhuaribao/macos/Runner/Configs/AppInfo.xcconfig
  36. 2 0
      xinhuaribao/macos/Runner/Configs/Debug.xcconfig
  37. 2 0
      xinhuaribao/macos/Runner/Configs/Release.xcconfig
  38. 13 0
      xinhuaribao/macos/Runner/Configs/Warnings.xcconfig
  39. 12 0
      xinhuaribao/macos/Runner/DebugProfile.entitlements
  40. 32 0
      xinhuaribao/macos/Runner/Info.plist
  41. 15 0
      xinhuaribao/macos/Runner/MainFlutterWindow.swift
  42. 8 0
      xinhuaribao/macos/Runner/Release.entitlements
  43. 12 0
      xinhuaribao/macos/RunnerTests/RunnerTests.swift
  44. 2 2
      xinhuaribao/pubspec.lock
  45. 1 1
      xinhuaribao/pubspec.yaml

+ 12 - 2
xinhuaribao/.claude/settings.local.json

@@ -5,11 +5,21 @@
       "Bash(tail -100 /Users/user/.claude/projects/-Users-user-Documents-Project-wwhale-xxf-demo-xinhuaribao/9b10ea82-ec16-4622-9a44-f28bc3e875e8/tool-results/*.txt)",
       "Bash(ls -la /private/tmp/claude-501/-Users-user-Documents-Project-wwhale-xxf-demo-xinhuaribao/9b10ea82-ec16-4622-9a44-f28bc3e875e8/tasks/*.output)",
       "Bash(flutter devices:*)",
-      "Bash(ls -la /private/tmp/claude-501/-Users-user-Documents-Project-wwhale-xxf/demo/xinhuaribao/9b10ea82-ec16-4622-9a44-f28bc3e875e8/tasks/*.output)",
+      "Bash(ls -la /private/tmp/claude-501/-Users-user-Documents-Project-wwhale/demo/xinhuaribao/9b10ea82-ec16-4622-9a44-f28bc3e875e8/tasks/*.output)",
       "Bash(pkill -f \"flutter run\")",
       "Bash(flutter clean:*)",
       "Bash(flutter pub:*)",
-      "Bash(flutter run:*)"
+      "Bash(flutter run:*)",
+      "Bash(flutter create:*)",
+      "Bash(flutter --version)",
+      "Bash(flutter emulators:*)",
+      "Bash(pkill -f \"pod install\")",
+      "Bash(xcrun simctl:*)",
+      "Bash(open -a Simulator)",
+      "Bash(tee /tmp/flutter_run_output.log)",
+      "Bash(pod install:*)",
+      "Bash(rm -rf ios/Pods ios/Podfile.lock ios/.symlinks ios/Flutter/.last_build_id)",
+      "Bash(git checkout:*)"
     ]
   }
 }

+ 6 - 9
xinhuaribao/.metadata

@@ -4,7 +4,7 @@
 # This file should be version controlled and should not be manually edited.
 
 version:
-  revision: "c23637390482d4cf9598c3ce3f2be31aa7332daf"
+  revision: "ff37bef603469fb030f2b72995ab929ccfc227f0"
   channel: "stable"
 
 project_type: app
@@ -13,14 +13,11 @@ project_type: app
 migration:
   platforms:
     - platform: root
-      create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
-      base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
-    - platform: android
-      create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
-      base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
-    - platform: ios
-      create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
-      base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
+      create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
+      base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
+    - platform: macos
+      create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
+      base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
 
   # User provided section
 

+ 186 - 0
xinhuaribao/CLAUDE.md

@@ -0,0 +1,186 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project Overview
+
+This is a Flutter news app (`news_app`) that displays news, videos, activities, and topics. The app uses:
+- **Flutter SDK 3.7.2+**
+- **Riverpod** for state management
+- **Dio** for HTTP requests
+- **go_router** for navigation
+- **flutter_screenutil** for responsive design (375x812 design size)
+- **EasyRefresh** for pull-to-refresh and load-more functionality
+
+## Development Commands
+
+```bash
+# Get dependencies
+flutter pub get
+
+# Run the app
+flutter run
+
+# Run on specific device (use `flutter devices` to list)
+flutter run -d <device-id>
+
+# Run on iOS simulator (auto-detect or use device-id)
+flutter run -d ios
+
+# Build APK (release)
+flutter build apk --release
+
+# Build APK with custom naming (uses build.py script)
+python3 build.py
+
+# Run tests
+flutter test
+
+# Analyze code
+flutter analyze
+
+# Generate code (for build_runner dependencies)
+flutter pub run build_runner build --delete-conflicting-outputs
+
+# Generate assets (for flutter_gen)
+flutter pub run build_runner watch --delete-conflicting-outputs
+```
+
+## Project Structure
+
+```
+lib/
+├── main.dart              # App entry point, router config, global providers
+├── constant/              # Configuration constants (API endpoints, config)
+├── http/                  # HTTP layer (Dio wrapper, exception handling)
+├── model/                 # Data models (JSON parsing with fromJson/toJson)
+├── provider/              # Riverpod providers (state management)
+├── ui/                   # UI pages (news, video, activity, topic, user, login, search)
+├── util/                  # Utilities (toast, cache, permissions, share, etc.)
+├── widget/                # Reusable widgets
+├── event/                 # Event bus events (e.g., LogoutEvent)
+├── extension/             # Dart extensions
+└── gen/                  # Generated assets (from flutter_gen)
+```
+
+## Architecture
+
+### State Management (Riverpod)
+
+The app uses Riverpod 2.x with `Notifier` and `FamilyNotifier` APIs (not deprecated `StateNotifierProvider`).
+
+Common patterns:
+
+1. **Global Providers** - defined in `main.dart`:
+   - `globalUserProvider` - current user session
+   - `globalThemeProvider` - theme mode (light/dark)
+   - `grayProvider` - grayscale mode toggle
+
+2. **Feature Providers** - located in `lib/provider/`, typically named `[feature]_provider.dart`:
+   - Each provider extends `Notifier<State>` or `FamilyNotifier<State, Arg>`
+   - State classes use immutable pattern with `copyWith()` method
+   - Example: `NewsNotifier` in `news_provider.dart`
+
+3. **Usage in widgets:**
+   ```dart
+   // Read state
+   final state = ref.watch(newsProvider(index));
+   // Call provider methods
+   ref.read(newsProvider(index).notifier).fetchNewsItemList(1);
+   ```
+
+### HTTP Layer (Dio)
+
+`lib/http/http_util.dart` wraps Dio for all API calls:
+
+- Singleton pattern: `HttpUtil()` instance
+- Base URL: configured in `lib/constant/api_const.dart`
+- Methods: `get()`, `post()`, `put()`, `delete()`, `upload()`, `download()`
+- Interceptors: adds `uuid` header for authentication, shows error toasts
+- Response handling: returns `data['data']` or throws `MyException` on `code != 200`
+- Error 401 triggers `LogoutEvent` via EventBus
+
+**API endpoints** are defined in `lib/constant/api_const.dart`.
+
+### Navigation (go_router)
+
+Router configuration in `lib/main.dart` (`_routerConfig`):
+
+- Routes defined as `GoRoute` with `path` and `builder`/`pageBuilder`
+- Page transitions use `CustomTransitionPage` with fade animations
+- Parameters passed via `state.extra` (not query params)
+- Example routes: `/login`, `/main`, `/news/detail`, `/video/detail`
+
+### Models
+
+All models in `lib/model/` use JSON serialization:
+- `fromJson(Map<String, dynamic> json)` - parses from API response
+- `toJson()` - serializes for API requests (if needed)
+
+### EventBus
+
+Global EventBus in `lib/constant/config.dart`:
+- Used for cross-component events (e.g., `LogoutEvent`)
+- Example: `eventBus.fire(LogoutEvent(jumLogin: true))`
+
+## Key Patterns
+
+### User Authentication
+
+- User UUID stored in `SharedPreferences` (see `lib/util/shared_prefs_instance_util.dart`)
+- UUID sent as `uuid` and `Authorization-M` headers in all requests
+- Global `uuid` variable in `lib/constant/config.dart`
+- Login state managed by `globalUserProvider`
+
+### Responsive Design
+
+All sizes use `flutter_screenutil`:
+- Design size: 375x812
+- Use `20.w`, `30.h`, `12.sp` for width/height/font-size
+- Wrap root with `ScreenUtilInit`
+
+### Refresh & Pagination
+
+- `EasyRefresh` for pull-to-refresh and load-more
+- Common pattern: fetch with `pageNum` parameter, append results on `pageNum > 1`
+
+### Grayscale Mode
+
+Controlled by `grayProvider`:
+- Wraps app in `ColorFiltered` with grayscale matrix when enabled
+- Configured via `enableProxy` flag in `lib/constant/config.dart` (also controls packet capture proxy)
+
+### Asset References
+
+Use generated assets from `lib/gen/assets.gen.dart`:
+```dart
+Image.asset(Assets.images.navNewsSelect.path)
+```
+
+### Video Playback
+
+The app includes video content using:
+- `better_player_plus` - Video player for streaming/local videos
+- `flutter_swiper_view` - Carousel/swiper for banners and image lists
+- Video pages handle autoplay, quality selection, and fullscreen modes
+
+### WeChat Integration
+
+Uses `fluwx` package for WeChat-related features:
+- Share content to WeChat moments/friends
+- WeChat login (if configured with app credentials
+
+## API Base URL
+
+Current base URL: `https://app.xhrbxxf.com/` (configured in `lib/constant/api_const.dart`)
+
+## Testing
+
+Tests are minimal (`test/widget_test.dart` contains a basic template).
+When adding tests, follow Flutter testing conventions and use `flutter test` to run.
+
+## Proxy/Debugging
+
+- Enable packet capture proxy by setting `enableProxy = true` in `lib/constant/config.dart`
+- Proxy address: `192.168.3.21:9090` (hardcoded)
+- PrettyDioLogger enabled in debug mode for request logging

+ 1 - 1
xinhuaribao/ios/Podfile

@@ -1,5 +1,5 @@
 # Uncomment this line to define a global platform for your project
-# platform :ios, '13.0'
+platform :ios, '13.0'
 
 # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
 ENV['COCOAPODS_DISABLE_STATS'] = 'true'

+ 5 - 5
xinhuaribao/ios/Podfile.lock

@@ -20,7 +20,7 @@ PODS:
     - fluwx/pay (= 0.0.1)
   - fluwx/pay (0.0.1):
     - Flutter
-    - WechatOpenSDK-XCFramework (~> 2.0.4)
+    - WechatOpenSDK-XCFramework (~> 2.0.5)
   - GCDWebServer (3.5.4):
     - GCDWebServer/Core (= 3.5.4)
   - GCDWebServer/Core (3.5.4)
@@ -65,7 +65,7 @@ PODS:
   - webview_flutter_wkwebview (0.0.1):
     - Flutter
     - FlutterMacOS
-  - WechatOpenSDK-XCFramework (2.0.4)
+  - WechatOpenSDK-XCFramework (2.0.5)
 
 DEPENDENCIES:
   - audio_session (from `.symlinks/plugins/audio_session/ios`)
@@ -145,7 +145,7 @@ SPEC CHECKSUMS:
   device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
   Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
   fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
-  fluwx: 6bf9c5a3a99ad31b0de137dd92370a0d10a60f4b
+  fluwx: 2ef787502fccb3f3596b380509001a8ea71cbbff
   GCDWebServer: 2c156a56c8226e2d5c0c3f208a3621ccffbe3ce4
   HLSCachingReverseProxyServer: 59935e1e0244ad7f3375d75b5ef46e8eb26ab181
   image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
@@ -162,8 +162,8 @@ SPEC CHECKSUMS:
   video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b
   wakelock_plus: 04623e3f525556020ebd4034310f20fe7fda8b49
   webview_flutter_wkwebview: 1821ceac936eba6f7984d89a9f3bcb4dea99ebb2
-  WechatOpenSDK-XCFramework: 36fb2bea0754266c17184adf4963d7e6ff98b69f
+  WechatOpenSDK-XCFramework: ff342ae616bb86df3d236aca38059dfd4bc4a949
 
-PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
+PODFILE CHECKSUM: 251cb053df7158f337c0712f2ab29f4e0fa474ce
 
 COCOAPODS: 1.16.2

+ 3 - 0
xinhuaribao/lib/constant/api_const.dart

@@ -189,6 +189,9 @@ final String apiSpecialDetail = '/api/cms/column/detail';
 //系统信息
 final String apiSystemInfo = '/api/system/info';
 
+final String apiUploadMulti = '/api/system/uploadMulti';
+
+
 //话题分享
 final String apiTopicShare = '/api/cms/content/topic/share';
 

+ 3 - 0
xinhuaribao/lib/constant/color_res.dart

@@ -70,3 +70,6 @@ const Color colorF9F9F9 = Color(0xFFF9F9F9);
 const Color color66748E = Color(0xFF66748E);
 
 const Color color001842 = Color(0xFF001842);
+
+const Color colorCCCCCC = Color(0xFFCCCCCC);
+const Color colorD9D9D9 = Color(0xFFD9D9D9);

+ 101 - 3
xinhuaribao/lib/http/http_util.dart

@@ -200,14 +200,21 @@ class HttpUtil {
     String path, {
     required String filePath,
     String? fileName,
+    String? siteId,
     Map<String, dynamic>? data,
     Options? options,
   }) async {
     try {
-      final formData = FormData.fromMap({
+      final formDataMap = <String, dynamic>{
         'file': await MultipartFile.fromFile(filePath, filename: fileName),
-        ...?data,
-      });
+      };
+      if (siteId != null) {
+        formDataMap['siteId'] = siteId;
+      }
+      if (data != null) {
+        formDataMap.addAll(data);
+      }
+      final formData = FormData.fromMap(formDataMap);
       final response = await _dio.post(path, data: formData, options: options);
       return _handleResponse(response);
     } catch (e) {
@@ -215,6 +222,97 @@ class HttpUtil {
     }
   }
 
+  /// 批量上传文件
+  Future<dynamic> uploadMultiple(
+    String path, {
+    required List<String> filePaths,
+    String fileField = 'files',
+    String? siteId,
+    Map<String, dynamic>? data,
+    Options? options,
+    ProgressCallback? onProgress,
+  }) async {
+    try {
+      final Map<String, dynamic> formDataMap = {...?data};
+
+      // 添加多个文件
+      final files = <MultipartFile>[];
+      for (int i = 0; i < filePaths.length; i++) {
+        files.add(await MultipartFile.fromFile(filePaths[i]));
+      }
+      formDataMap[fileField] = files;
+
+      if (siteId != null) {
+        formDataMap['siteId'] = siteId;
+      }
+
+      final formData = FormData.fromMap(formDataMap);
+      final response = await _dio.post(
+        path,
+        data: formData,
+        options: options,
+        onSendProgress: onProgress,
+      );
+      return _handleResponse(response);
+    } catch (e) {
+      return _handleError(e);
+    }
+  }
+
+  /// 上传单个图片(评论用)
+  Future<String?> uploadCommentImage(String filePath, {String? siteId}) async {
+    try {
+      final result = await upload(
+        '/api/system/upload',
+        filePath: filePath,
+        siteId: siteId,
+      );
+      // 假设返回格式为 { url: 'xxx' } 或直接返回字符串 url
+      if (result is String) {
+        return result;
+      } else if (result is Map && result['url'] != null) {
+        return result['url'] as String?;
+      }
+      return null;
+    } catch (e) {
+      debugPrint('图片上传失败: $e');
+      return null;
+    }
+  }
+
+  /// 批量上传图片(评论用)
+  Future<List<String>> uploadCommentImages(List<String> filePaths, {String? siteId}) async {
+    try {
+      final result = await uploadMultiple(
+        '/api/system/uploadMulti',
+        filePaths: filePaths,
+        fileField: 'files',
+        siteId: siteId,
+      );
+      // 假设返回格式为 [url1, url2, ...] 或 { urls: [url1, url2, ...] }
+      if (result is List) {
+        return result.cast<String>();
+      } else if (result is Map && result['urls'] != null) {
+        final urls = result['urls'];
+        if (urls is List) {
+          return urls.cast<String>();
+        }
+      } else if (result is Map && result['url'] != null) {
+        // 单张图片的情况
+        final url = result['url'];
+        if (url is List) {
+          return url.cast<String>();
+        } else if (url is String) {
+          return [url];
+        }
+      }
+      return [];
+    } catch (e) {
+      debugPrint('批量上传图片失败: $e');
+      return [];
+    }
+  }
+
   /// 文件下载
   Future<dynamic> download(
     String url,

+ 14 - 14
xinhuaribao/lib/main.dart

@@ -215,7 +215,7 @@ class _MainPageState extends ConsumerState<MainPage> {
           MainNewsPage(),
           MainActivityPage(),
           MainVideoPage(),
-          // MainTopicPage(),
+          MainTopicPage(),
           MainUserPage(),
         ],
       ),
@@ -265,19 +265,19 @@ class _MainPageState extends ConsumerState<MainPage> {
               height: 20.w,
             ),
           ),
-          // BottomNavigationBarItem(
-          //   icon: Image.asset(
-          //     Assets.images.navTopicUnselect.path,
-          //     width: 20.w,
-          //     height: 20.w,
-          //   ),
-          //   label: "话题",
-          //   activeIcon: Image.asset(
-          //     Assets.images.navTopicSelect.path,
-          //     width: 20.w,
-          //     height: 20.w,
-          //   ),
-          // ),
+          BottomNavigationBarItem(
+            icon: Image.asset(
+              Assets.images.navTopicUnselect.path,
+              width: 20.w,
+              height: 20.w,
+            ),
+            label: "话题",
+            activeIcon: Image.asset(
+              Assets.images.navTopicSelect.path,
+              width: 20.w,
+              height: 20.w,
+            ),
+          ),
           BottomNavigationBarItem(
             icon: Image.asset(
               Assets.images.navUserUnselect.path,

+ 13 - 0
xinhuaribao/lib/model/new_comment_model.dart

@@ -86,6 +86,7 @@ class Comment {
     String? content,
     String? createTime,
     List<SubComment>? subComment,
+    List<dynamic>? resourceUrls,
   }) {
     _contentId = contentId;
     _commentId = commentId;
@@ -93,6 +94,7 @@ class Comment {
     _content = content;
     _createTime = createTime;
     _subComment = subComment;
+    _resourceUrls = resourceUrls;
   }
 
   Comment.fromJson(dynamic json) {
@@ -108,6 +110,9 @@ class Comment {
         _subComment?.add(SubComment.fromJson(v));
       });
     }
+    if (json['resourceUrls'] != null) {
+      _resourceUrls = json['resourceUrls'];
+    }
   }
 
   String? _contentId;
@@ -116,6 +121,7 @@ class Comment {
   String? _content;
   String? _createTime;
   List<SubComment>? _subComment;
+  List<dynamic>? _resourceUrls;
 
   Comment copyWith({
     String? contentId,
@@ -124,6 +130,7 @@ class Comment {
     String? content,
     String? createTime,
     List<SubComment>? subComment,
+    List<dynamic>? resourceUrls,
   }) => Comment(
     contentId:contentId ?? _contentId,
     commentId: commentId ?? _commentId,
@@ -131,6 +138,7 @@ class Comment {
     content: content ?? _content,
     createTime: createTime ?? _createTime,
     subComment: subComment ?? _subComment,
+    resourceUrls: resourceUrls ?? _resourceUrls,
   );
 
   String? get contentId => _contentId;
@@ -145,6 +153,8 @@ class Comment {
 
   List<SubComment>? get subComment => _subComment;
 
+  List<dynamic>? get resourceUrls => _resourceUrls;
+
   Map<String, dynamic> toJson() {
     final map = <String, dynamic>{};
     if (_fromUser != null) {
@@ -157,6 +167,9 @@ class Comment {
     if (_subComment != null) {
       map['subComment'] = _subComment?.map((v) => v.toJson()).toList();
     }
+    if (_resourceUrls != null) {
+      map['resourceUrls'] = _resourceUrls;
+    }
     return map;
   }
 }

+ 1 - 0
xinhuaribao/lib/provider/common_provider.dart

@@ -45,6 +45,7 @@ class CommentNotifier extends FamilyNotifier<NewCommentModel, CommentType> {
         "content": comment.content,
         "commentId": "",
         "replyId": "",
+        "resources": comment.resourceUrls?.join(','),
       },
     );
     if (jsonData != null ){

+ 301 - 37
xinhuaribao/lib/ui/video/comment_input_bar_widget.dart

@@ -1,8 +1,13 @@
+import 'dart:io';
+
+import 'package:cached_network_image/cached_network_image.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_riverpod/flutter_riverpod.dart';
 import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:image_picker/image_picker.dart';
 import 'package:news_app/constant/color_res.dart';
 import 'package:news_app/constant/size_res.dart';
+import 'package:news_app/http/http_util.dart';
 import 'package:news_app/widget/my_txt.dart';
 
 import '../../widget/auth_gesture_detector.dart';
@@ -12,12 +17,44 @@ import '../../widget/auth_gesture_detector.dart';
 /// @date: 2025 2025/4/17 12:22
 /// @description:
 
+/// 图片上传状态
+enum UploadStatus {
+  uploading, // 上传中
+  success, // 上传成功
+  failed, // 上传失败
+}
+
+/// 图片数据模型
+class ImageItem {
+  final String localPath; // 本地路径
+  String? remoteUrl; // 远程URL
+  UploadStatus status; // 上传状态
+
+  ImageItem({
+    required this.localPath,
+    this.remoteUrl,
+    this.status = UploadStatus.uploading,
+  });
+
+  ImageItem copyWith({
+    String? localPath,
+    String? remoteUrl,
+    UploadStatus? status,
+  }) {
+    return ImageItem(
+      localPath: localPath ?? this.localPath,
+      remoteUrl: remoteUrl ?? this.remoteUrl,
+      status: status ?? this.status,
+    );
+  }
+}
+
 class CommentInputBarWidget extends ConsumerStatefulWidget {
   final FocusNode focusNode;
 
   final String id;
 
-  final Function(String)? onSend;
+  final Function(String, List<String>)? onSend; // 改为传递URL列表
 
   const CommentInputBarWidget(
     this.focusNode,
@@ -33,6 +70,94 @@ class CommentInputBarWidget extends ConsumerStatefulWidget {
 
 class _CommentInputBarState extends ConsumerState<CommentInputBarWidget> {
   final TextEditingController _controller = TextEditingController();
+  final List<ImageItem> _imageItems = [];
+  static const int maxImageCount = 9;
+
+  Future<void> _pickImage() async {
+    final List<XFile> pickedFiles = await ImagePicker().pickMultiImage(
+      imageQuality: 80,
+    );
+
+    if (pickedFiles.isNotEmpty) {
+      // 计算还能添加多少张图片
+      final remainingSlots = maxImageCount - _imageItems.length;
+      if (remainingSlots <= 0) {
+        // 已达到最大数量,不添加
+        return;
+      }
+      // 添加新图片,最多添加到9张
+      final imagesToAdd = pickedFiles.take(remainingSlots).toList();
+
+      setState(() {
+        for (var file in imagesToAdd) {
+          _imageItems.add(ImageItem(localPath: file.path));
+        }
+      });
+
+      // 立即上传图片
+      _uploadImages(imagesToAdd.map((e) => e.path).toList());
+    }
+  }
+
+  /// 上传图片
+  Future<void> _uploadImages(List<String> filePaths) async {
+    // 批量上传接口
+    final urls = await HttpUtil().uploadCommentImages(filePaths);
+
+    setState(() {
+      // 更新上传状态
+      int urlIndex = 0;
+      for (int i = 0; i < _imageItems.length; i++) {
+        if (_imageItems[i].status == UploadStatus.uploading &&
+            filePaths.contains(_imageItems[i].localPath)) {
+          if (urlIndex < urls.length) {
+            _imageItems[i] = _imageItems[i].copyWith(
+              remoteUrl: urls[urlIndex],
+              status: UploadStatus.success,
+            );
+            urlIndex++;
+          } else {
+            _imageItems[i] = _imageItems[i].copyWith(
+              status: UploadStatus.failed,
+            );
+          }
+        }
+      }
+    });
+  }
+
+  /// 重试上传失败的图片
+  Future<void> _retryUpload(int index) async {
+    final item = _imageItems[index];
+    setState(() {
+      _imageItems[index] = item.copyWith(status: UploadStatus.uploading);
+    });
+
+    final url = await HttpUtil().uploadCommentImage(item.localPath);
+
+    setState(() {
+      if (url != null) {
+        _imageItems[index] = item.copyWith(
+          remoteUrl: url,
+          status: UploadStatus.success,
+        );
+      } else {
+        _imageItems[index] = item.copyWith(status: UploadStatus.failed);
+      }
+    });
+  }
+
+  void _removeImage(int index) {
+    setState(() {
+      _imageItems.removeAt(index);
+    });
+  }
+
+  void _clearImages() {
+    setState(() {
+      _imageItems.clear();
+    });
+  }
 
   @override
   Widget build(BuildContext context) {
@@ -46,49 +171,188 @@ class _CommentInputBarState extends ConsumerState<CommentInputBarWidget> {
         top: horizontalPadding,
         bottom: size > 0 ? size : horizontalPadding,
       ),
-      child: Row(
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.end,
+        mainAxisSize: MainAxisSize.min,
         children: [
-          Expanded(
-            child: Container(
-              height: 40.h,
-              margin: EdgeInsets.only(right: 10.w),
-              child: TextField(
-                decoration: InputDecoration(
-                  fillColor: colorF5F7FA,
-                  filled: true,
-                  border: OutlineInputBorder(
-                    borderRadius: BorderRadius.circular(5.r),
-                    borderSide: BorderSide.none,
+          // 图片预览区域 - 支持多张图片
+          if (_imageItems.isNotEmpty)
+            Container(
+              height: 60.h,
+              margin: EdgeInsets.only(bottom: 10.w),
+              child: SingleChildScrollView(
+                scrollDirection: Axis.horizontal,
+                child: Row(
+                  children: List.generate(_imageItems.length, (index) {
+                    final item = _imageItems[index];
+                    return Container(
+                      width: 60.w,
+                      height: 60.h,
+                      margin: EdgeInsets.only(right: 8.w),
+                      child: Stack(
+                        alignment: Alignment.topRight,
+                        children: [
+                          // 显示图片(本地或远程)
+                          ClipRRect(
+                            borderRadius: BorderRadius.circular(4.r),
+                            child: item.status == UploadStatus.success && item.remoteUrl != null
+                                ? CachedNetworkImage(
+                                    imageUrl: item.remoteUrl!,
+                                    width: 60.w,
+                                    height: 60.h,
+                                    fit: BoxFit.cover,
+                                    placeholder: (context, url) => Image.file(
+                                      File(item.localPath),
+                                      width: 60.w,
+                                      height: 60.h,
+                                      fit: BoxFit.cover,
+                                    ),
+                                    errorWidget: (context, url, error) => Image.file(
+                                      File(item.localPath),
+                                      width: 60.w,
+                                      height: 60.h,
+                                      fit: BoxFit.cover,
+                                    ),
+                                  )
+                                : Image.file(
+                                    File(item.localPath),
+                                    width: 60.w,
+                                    height: 60.h,
+                                    fit: BoxFit.cover,
+                                  ),
+                          ),
+                          // 上传中遮罩
+                          if (item.status == UploadStatus.uploading)
+                            Container(
+                              width: 60.w,
+                              height: 60.h,
+                              decoration: BoxDecoration(
+                                color: Colors.black26,
+                                borderRadius: BorderRadius.circular(4.r),
+                              ),
+                              child: Center(
+                                child: SizedBox(
+                                  width: 20.w,
+                                  height: 20.w,
+                                  child: CircularProgressIndicator(
+                                    strokeWidth: 2,
+                                    valueColor:
+                                        AlwaysStoppedAnimation<Color>(Colors.white),
+                                  ),
+                                ),
+                              ),
+                            ),
+                          // 上传失败标记
+                          if (item.status == UploadStatus.failed)
+                            Container(
+                              width: 60.w,
+                              height: 60.h,
+                              decoration: BoxDecoration(
+                                color: Colors.black26,
+                                borderRadius: BorderRadius.circular(4.r),
+                              ),
+                              child: Center(
+                                child: GestureDetector(
+                                  onTap: () => _retryUpload(index),
+                                  child: Icon(
+                                    Icons.refresh,
+                                    color: Colors.white,
+                                    size: 20.w,
+                                  ),
+                                ),
+                              ),
+                            ),
+                          // 删除按钮
+                          GestureDetector(
+                            onTap: () => _removeImage(index),
+                            child: Container(
+                              margin: EdgeInsets.all(2.w),
+                              width: 16.w,
+                              height: 16.w,
+                              decoration: BoxDecoration(
+                                color: Colors.black54,
+                                shape: BoxShape.circle,
+                              ),
+                              child: Icon(
+                                Icons.close,
+                                color: Colors.white,
+                                size: 10.w,
+                              ),
+                            ),
+                          ),
+                        ],
+                      ),
+                    );
+                  }),
+                ),
+              ),
+            ),
+          Row(
+            children: [
+              Expanded(
+                child: Container(
+                  height: 40.h,
+                  margin: EdgeInsets.only(right: 10.w),
+                  child: TextField(
+                    decoration: InputDecoration(
+                      fillColor: colorF5F7FA,
+                      filled: true,
+                      border: OutlineInputBorder(
+                        borderRadius: BorderRadius.circular(5.r),
+                        borderSide: BorderSide.none,
+                      ),
+                      contentPadding: EdgeInsets.symmetric(
+                        horizontal: 10.w,
+                        vertical: 20.h,
+                      ),
+                      hintText: '快来写下你的评论吧~',
+                      hintStyle: TextStyle(color: color7788A0, fontSize: 12.sp),
+                    ),
+                    focusNode: widget.focusNode,
+                    controller: _controller,
                   ),
-                  contentPadding: EdgeInsets.symmetric(
-                    horizontal: 10.w,
-                    vertical: 20.h,
+                ),
+              ),
+              // 图片上传按钮
+              GestureDetector(
+                onTap: _pickImage,
+                child: Container(
+                  width: 40.w,
+                  height: 40.h,
+                  margin: EdgeInsets.only(right: 8.w),
+                  alignment: Alignment.center,
+                  child: Icon(
+                    Icons.image,
+                    color: _imageItems.isNotEmpty ? color188FFF : color7788A0,
+                    size: 24.w,
                   ),
-                  hintText: '快来写下你的评论吧~',
-                  hintStyle: TextStyle(color: color7788A0, fontSize: 12.sp),
                 ),
-                focusNode: widget.focusNode,
-                controller: _controller,
               ),
-            ),
-          ),
-          if (widget.focusNode.hasFocus)
-            AuthGestureDetector(
-              onTap: () {
-                widget.onSend?.call(_controller.text);
-                _controller.clear();
-              },
-              child: Container(
-                width: 50.w,
-                padding: EdgeInsets.symmetric(vertical: 5.h),
-                alignment: Alignment.center,
-                decoration: BoxDecoration(
-                  color: color188FFF,
-                  borderRadius: BorderRadius.circular(5.r),
+              AuthGestureDetector(
+                onTap: () {
+                  // 获取所有上传成功的图片URL
+                  final successUrls = _imageItems
+                      .where((item) =>
+                          item.status == UploadStatus.success && item.remoteUrl != null)
+                      .map((item) => item.remoteUrl!)
+                      .toList();
+                  widget.onSend?.call(_controller.text, successUrls);
+                  _controller.clear();
+                  _clearImages();
+                },
+                child: Container(
+                  width: 50.w,
+                  padding: EdgeInsets.symmetric(vertical: 5.h),
+                  alignment: Alignment.center,
+                  decoration: BoxDecoration(
+                    color: color188FFF,
+                    borderRadius: BorderRadius.circular(5.r),
+                  ),
+                  child: myTxt(text: "发送", color: Colors.white, fontSize: 12.sp),
                 ),
-                child: myTxt(text: "发送", color: Colors.white, fontSize: 12.sp),
               ),
-            ),
+            ],
+          ),
         ],
       ),
     );

+ 266 - 60
xinhuaribao/lib/ui/video/comment_item_widget.dart

@@ -1,3 +1,4 @@
+import 'package:cached_network_image/cached_network_image.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_riverpod/flutter_riverpod.dart';
 import 'package:flutter_screenutil/flutter_screenutil.dart';
@@ -12,6 +13,218 @@ import '../../widget/auth_gesture_detector.dart';
 /// @date: 2025 2025/4/17 12:22
 /// @description:
 
+/// 评论图片网格组件 - 参考微信朋友圈布局
+class CommentImagesGrid extends StatelessWidget {
+  final List<dynamic> images;
+
+  const CommentImagesGrid({super.key, required this.images});
+
+  @override
+  Widget build(BuildContext context) {
+    if (images.isEmpty) return const SizedBox.shrink();
+
+    final count = images.length;
+    double itemWidth = 0;
+    double itemHeight = 0;
+
+    // 根据图片数量计算尺寸(微信朋友圈风格)
+    switch (count) {
+      case 1:
+        itemWidth = 200.w;
+        itemHeight = 200.w;
+        break;
+      case 2:
+        itemWidth = 150.w;
+        itemHeight = 150.w;
+        break;
+      case 3:
+      case 4:
+        itemWidth = 130.w;
+        itemHeight = 130.w;
+        break;
+      default:
+        // 5-9张图片
+        itemWidth = 100.w;
+        itemHeight = 100.w;
+        break;
+    }
+
+    // 计算列数
+    int crossAxisCount = 1;
+    if (count == 2 || count == 4) {
+      crossAxisCount = 2;
+    } else if (count >= 3) {
+      crossAxisCount = count <= 4 ? 2 : 3;
+    }
+
+    return GridView.builder(
+      shrinkWrap: true,
+      physics: const NeverScrollableScrollPhysics(),
+      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
+        crossAxisCount: crossAxisCount,
+        mainAxisSpacing: 5.w,
+        crossAxisSpacing: 5.w,
+        childAspectRatio: 1,
+      ),
+      itemCount: count,
+      itemBuilder: (context, index) {
+        final imgUrl = images[index];
+        return GestureDetector(
+          onTap: () => _previewImage(context, images, index),
+          child: ClipRRect(
+            borderRadius: BorderRadius.circular(4.r),
+            child: CachedNetworkImage(
+              imageUrl: imgUrl.toString(),
+              width: itemWidth,
+              height: itemHeight,
+              fit: BoxFit.cover,
+              placeholder: (context, url) => Container(
+                width: itemWidth,
+                height: itemHeight,
+                color: colorF5F7FA,
+                child: Icon(Icons.image, color: colorCCCCCC, size: 30.w),
+              ),
+              errorWidget: (context, url, error) => Container(
+                width: itemWidth,
+                height: itemHeight,
+                color: colorF5F7FA,
+                child: Icon(Icons.broken_image, color: colorCCCCCC, size: 30.w),
+              ),
+            ),
+          ),
+        );
+      },
+    );
+  }
+
+  /// 预览图片
+  void _previewImage(BuildContext context, List<dynamic> images, int index) {
+    Navigator.of(context).push(
+      PageRouteBuilder(
+        pageBuilder: (context, animation, secondaryAnimation) => ImageViewerPage(
+          images: images.map((e) => e.toString()).toList(),
+          initialIndex: index,
+        ),
+        transitionsBuilder: (context, animation, secondaryAnimation, child) {
+          return FadeTransition(
+            opacity: animation,
+            child: child,
+          );
+        },
+      ),
+    );
+  }
+}
+
+/// 图片预览页面
+class ImageViewerPage extends StatefulWidget {
+  final List<String> images;
+  final int initialIndex;
+
+  const ImageViewerPage({
+    super.key,
+    required this.images,
+    required this.initialIndex,
+  });
+
+  @override
+  State<ImageViewerPage> createState() => _ImageViewerPageState();
+}
+
+class _ImageViewerPageState extends State<ImageViewerPage> {
+  late PageController _pageController;
+  late int _currentIndex;
+
+  @override
+  void initState() {
+    super.initState();
+    _currentIndex = widget.initialIndex;
+    _pageController = PageController(initialPage: _currentIndex);
+  }
+
+  @override
+  void dispose() {
+    _pageController.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      backgroundColor: Colors.black,
+      body: Stack(
+        children: [
+          // 图片预览
+          PageView.builder(
+            controller: _pageController,
+            itemCount: widget.images.length,
+            onPageChanged: (index) {
+              setState(() {
+                _currentIndex = index;
+              });
+            },
+            itemBuilder: (context, index) {
+              return Center(
+                child: CachedNetworkImage(
+                  imageUrl: widget.images[index],
+                  fit: BoxFit.contain,
+                  placeholder: (context, url) => Center(
+                    child: CircularProgressIndicator(color: Colors.white),
+                  ),
+                  errorWidget: (context, url, error) => Center(
+                    child: Icon(Icons.broken_image, color: Colors.white, size: 50),
+                  ),
+                ),
+              );
+            },
+          ),
+          // 关闭按钮
+          Positioned(
+            top: MediaQuery.of(context).padding.top + 10,
+            right: 10,
+            child: GestureDetector(
+              onTap: () => Navigator.pop(context),
+              child: Container(
+                padding: EdgeInsets.all(8.w),
+                decoration: BoxDecoration(
+                  color: Colors.black54,
+                  shape: BoxShape.circle,
+                ),
+                child: Icon(Icons.close, color: Colors.white, size: 24.w),
+              ),
+            ),
+          ),
+          // 图片指示器
+          if (widget.images.length > 1)
+            Positioned(
+              bottom: MediaQuery.of(context).padding.bottom + 20,
+              left: 0,
+              right: 0,
+              child: Center(
+                child: Row(
+                  mainAxisAlignment: MainAxisAlignment.center,
+                  children: List.generate(
+                    widget.images.length,
+                    (index) => AnimatedContainer(
+                      duration: const Duration(milliseconds: 200),
+                      margin: EdgeInsets.symmetric(horizontal: 3.w),
+                      width: _currentIndex == index ? 8.w : 6.w,
+                      height: 6.w,
+                      decoration: BoxDecoration(
+                        color: _currentIndex == index ? Colors.white : colorF5F7FA,
+                        borderRadius: BorderRadius.circular(3.r),
+                      ),
+                    ),
+                  ),
+                ),
+              ),
+            ),
+        ],
+      ),
+    );
+  }
+}
+
 class CommentItemWidget extends ConsumerStatefulWidget {
   //加一个回调函数
   final Function(int, String)? onTap;
@@ -20,7 +233,8 @@ class CommentItemWidget extends ConsumerStatefulWidget {
   final int commentIndex;
 
   const CommentItemWidget(
-    this.onTap,this.onLongTap,{
+    this.onTap,
+    this.onLongTap, {
     super.key,
     required this.comment,
     required this.commentIndex,
@@ -64,6 +278,15 @@ class _CommentItemWidgetState extends ConsumerState<CommentItemWidget> {
                     widget.comment.content ?? "",
                     style: TextStyle(color: color333333, fontSize: 14.sp),
                   ),
+                  // 图片展示
+                  if (widget.comment.resourceUrls != null &&
+                      widget.comment.resourceUrls!.isNotEmpty)
+                    Padding(
+                      padding: EdgeInsets.only(top: 8.h),
+                      child: CommentImagesGrid(
+                        images: widget.comment.resourceUrls!,
+                      ),
+                    ),
                   Row(
                     spacing: 10.w,
                     children: [
@@ -83,7 +306,9 @@ class _CommentItemWidgetState extends ConsumerState<CommentItemWidget> {
                           style: TextStyle(color: color333333, fontSize: 12.sp),
                         ),
                       ),
-                      SizedBox(width:5.w,),
+                      SizedBox(
+                        width: 5.w,
+                      ),
                       AuthGestureDetector(
                         onTap: () {
                           widget.onLongTap?.call(
@@ -108,76 +333,57 @@ class _CommentItemWidgetState extends ConsumerState<CommentItemWidget> {
             padding: EdgeInsets.only(left: 30.w, top: 10.h),
             child: Column(
               crossAxisAlignment: CrossAxisAlignment.start,
-              children:
-                  widget.comment.subComment!.map((sub) {
-                    return Padding(
-                      padding: EdgeInsets.only(bottom: 8.h),
-                      child: Row(
-                        crossAxisAlignment: CrossAxisAlignment.start,
-                        children: [
-                          CircleAvatar(
-                            radius: 13.w,
-                            backgroundImage: NetworkImage(
-                              widget.comment.fromUser?.avatar ?? "",
+              children: widget.comment.subComment!.map((sub) {
+                return Padding(
+                  padding: EdgeInsets.only(bottom: 8.h),
+                  child: Row(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    children: [
+                      CircleAvatar(
+                        radius: 13.w,
+                        backgroundImage: NetworkImage(
+                          widget.comment.fromUser?.avatar ?? "",
+                        ),
+                      ),
+                      SizedBox(width: 5.w),
+                      Expanded(
+                        child: Column(
+                          crossAxisAlignment: CrossAxisAlignment.start,
+                          spacing: 4.h,
+                          children: [
+                            Text(
+                              sub.fromUser?.nickname ?? "",
+                              style: TextStyle(
+                                fontWeight: FontWeight.bold,
+                                fontSize: 14.sp,
+                              ),
                             ),
-                          ),
-                          SizedBox(width: 5.w),
-                          Expanded(
-                            child: Column(
-                              crossAxisAlignment: CrossAxisAlignment.start,
-                              spacing: 4.h,
+                            Text(
+                              sub.content ?? "",
+                              style: TextStyle(fontSize: 14.sp),
+                            ),
+                            Row(
+                              spacing: 10.w,
                               children: [
                                 Text(
-                                  //'${reply.name} 回复 ${reply.replyTo}:',
-                                  sub.fromUser?.nickname ?? "",
+                                  sub.createTime ?? "",
                                   style: TextStyle(
-                                    fontWeight: FontWeight.bold,
-                                    fontSize: 14.sp,
+                                    color: color7788A0,
+                                    fontSize: 12.sp,
                                   ),
                                 ),
-                                Text(
-                                  sub.content ?? "",
-                                  style: TextStyle(fontSize: 14.sp),
-                                ),
-                                Row(
-                                  spacing: 10.w,
-                                  children: [
-                                    Text(
-                                      sub.createTime ?? "",
-                                      style: TextStyle(
-                                        color: color7788A0,
-                                        fontSize: 12.sp,
-                                      ),
-                                    ),
-                                 /*   GestureDetector(
-                                      onTap: () {
-                                        widget.onTap?.call(
-                                          widget.commentIndex,
-                                          //reply.name,
-                                          sub.fromUser?.nickname ?? "",
-                                        );
-                                      },
-                                      child: Text(
-                                        '回复',
-                                        style: TextStyle(
-                                          color: color333333,
-                                          fontSize: 12.sp,
-                                        ),
-                                      ),
-                                    ),*/
-                                  ],
-                                ),
                               ],
                             ),
-                          ),
-                        ],
+                          ],
+                        ),
                       ),
-                    );
-                  }).toList(),
+                    ],
+                  ),
+                );
+              }).toList(),
             ),
           ),
       ],
     );
   }
 }
-

+ 4 - 2
xinhuaribao/lib/ui/video/comment_page.dart

@@ -137,11 +137,12 @@ class _CommentPageSate extends ConsumerState<CommentPage>
     notifier.fetchComment(page: _pageSize, articleId: widget.articleId);
   }
 
-  void sendComment(String content) {
+  void sendComment(String content, List<String> images) {
     KeyboardUtils.hideKeyboard(_focusNode);
-    consoleLog("replyName:$replyName content:$content");
+    consoleLog("replyName:$replyName content:$content images:$images");
     UserModel user = ref.read(globalUserProvider);
     final notifier = ref.read(commentProvider(widget.type).notifier);
+
     if (replyName.isEmpty) {
       //新增评论
       notifier.addComment(
@@ -154,6 +155,7 @@ class _CommentPageSate extends ConsumerState<CommentPage>
           ),
           content: content,
           createTime: DateUtil.getNowDateStr(),
+          resourceUrls: images.isNotEmpty ? images : null,
         ),
       );
     } else {

+ 2 - 2
xinhuaribao/lib/util/share_util.dart

@@ -48,7 +48,7 @@ Future<void> shareWeiXinPYUrl({
       url,
       thumbData: imageBytes,
       title: title,
-      description: "新华新消费",
+      description: "新华报业传媒集团",
       scene: WeChatScene.timeline,
     );
 
@@ -84,7 +84,7 @@ Future<void> shareWeiXinUrl({
       url,
       thumbData: imageBytes,
       title: title,
-      description: "新华新消费",
+      description: "新华报业传媒集团",
       scene: WeChatScene.session,
     );
 

+ 7 - 0
xinhuaribao/macos/.gitignore

@@ -0,0 +1,7 @@
+# Flutter-related
+**/Flutter/ephemeral/
+**/Pods/
+
+# Xcode-related
+**/dgph
+**/xcuserdata/

+ 2 - 0
xinhuaribao/macos/Flutter/Flutter-Debug.xcconfig

@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "ephemeral/Flutter-Generated.xcconfig"

+ 2 - 0
xinhuaribao/macos/Flutter/Flutter-Release.xcconfig

@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "ephemeral/Flutter-Generated.xcconfig"

+ 106 - 0
xinhuaribao/macos/Podfile.lock

@@ -0,0 +1,106 @@
+PODS:
+  - audio_session (0.0.1):
+    - FlutterMacOS
+  - connectivity_plus (0.0.1):
+    - FlutterMacOS
+  - device_info_plus (0.0.1):
+    - FlutterMacOS
+  - file_selector_macos (0.0.1):
+    - FlutterMacOS
+  - FlutterMacOS (1.0.0)
+  - just_audio (0.0.1):
+    - Flutter
+    - FlutterMacOS
+  - package_info_plus (0.0.1):
+    - FlutterMacOS
+  - path_provider_foundation (0.0.1):
+    - Flutter
+    - FlutterMacOS
+  - share_plus (0.0.1):
+    - FlutterMacOS
+  - shared_preferences_foundation (0.0.1):
+    - Flutter
+    - FlutterMacOS
+  - sqflite_darwin (0.0.4):
+    - Flutter
+    - FlutterMacOS
+  - url_launcher_macos (0.0.1):
+    - FlutterMacOS
+  - video_player_avfoundation (0.0.1):
+    - Flutter
+    - FlutterMacOS
+  - wakelock_plus (0.0.1):
+    - FlutterMacOS
+  - webview_flutter_wkwebview (0.0.1):
+    - Flutter
+    - FlutterMacOS
+
+DEPENDENCIES:
+  - audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`)
+  - connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
+  - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
+  - file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`)
+  - FlutterMacOS (from `Flutter/ephemeral`)
+  - just_audio (from `Flutter/ephemeral/.symlinks/plugins/just_audio/darwin`)
+  - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
+  - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
+  - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
+  - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
+  - sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`)
+  - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
+  - video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`)
+  - wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`)
+  - webview_flutter_wkwebview (from `Flutter/ephemeral/.symlinks/plugins/webview_flutter_wkwebview/darwin`)
+
+EXTERNAL SOURCES:
+  audio_session:
+    :path: Flutter/ephemeral/.symlinks/plugins/audio_session/macos
+  connectivity_plus:
+    :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos
+  device_info_plus:
+    :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
+  file_selector_macos:
+    :path: Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos
+  FlutterMacOS:
+    :path: Flutter/ephemeral
+  just_audio:
+    :path: Flutter/ephemeral/.symlinks/plugins/just_audio/darwin
+  package_info_plus:
+    :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
+  path_provider_foundation:
+    :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
+  share_plus:
+    :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
+  shared_preferences_foundation:
+    :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
+  sqflite_darwin:
+    :path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin
+  url_launcher_macos:
+    :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
+  video_player_avfoundation:
+    :path: Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin
+  wakelock_plus:
+    :path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos
+  webview_flutter_wkwebview:
+    :path: Flutter/ephemeral/.symlinks/plugins/webview_flutter_wkwebview/darwin
+
+SPEC CHECKSUMS:
+  audio_session: eaca2512cf2b39212d724f35d11f46180ad3a33e
+  connectivity_plus: 4adf20a405e25b42b9c9f87feff8f4b6fde18a4e
+  device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76
+  file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31
+  FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
+  just_audio: 4e391f57b79cad2b0674030a00453ca5ce817eed
+  package_info_plus: f0052d280d17aa382b932f399edf32507174e870
+  path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
+  share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc
+  shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
+  sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
+  url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673
+  video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b
+  wakelock_plus: 21ddc249ac4b8d018838dbdabd65c5976c308497
+  webview_flutter_wkwebview: 1821ceac936eba6f7984d89a9f3bcb4dea99ebb2
+
+PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009
+
+COCOAPODS: 1.16.2

+ 801 - 0
xinhuaribao/macos/Runner.xcodeproj/project.pbxproj

@@ -0,0 +1,801 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 54;
+	objects = {
+
+/* Begin PBXAggregateTarget section */
+		33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
+			isa = PBXAggregateTarget;
+			buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
+			buildPhases = (
+				33CC111E2044C6BF0003C045 /* ShellScript */,
+			);
+			dependencies = (
+			);
+			name = "Flutter Assemble";
+			productName = FLX;
+		};
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+		29046788D63B65F2AC9DDCE8 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A6694D74111009850CB5E4A1 /* Pods_Runner.framework */; };
+		2ED36BBC6E99E67A51AACA58 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F2F80C765F88DBFD9B91EC0 /* Pods_RunnerTests.framework */; };
+		331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
+		335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
+		33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
+		33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
+		33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
+		33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 33CC10EC2044A3C60003C045;
+			remoteInfo = Runner;
+		};
+		33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 33CC111A2044C6BA0003C045;
+			remoteInfo = FLX;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		33CC110E2044A8840003C045 /* Bundle Framework */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+			);
+			name = "Bundle Framework";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		1034281CAB2A67F991E61008 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
+		11094761CF179F8D93B12C08 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
+		2F2F80C765F88DBFD9B91EC0 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
+		333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
+		335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
+		33CC10ED2044A3C60003C045 /* news_app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = news_app.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+		33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
+		33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
+		33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
+		33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
+		33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
+		33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
+		33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
+		33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
+		33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
+		33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
+		4B9071A6D9EBBE615E8B5867 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
+		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
+		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
+		A6694D74111009850CB5E4A1 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		C4497D3A28954422828E6444 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
+		F4E92A4EDEE5AF3A26B783CC /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
+		FF3A7B2320378B0990411380 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		331C80D2294CF70F00263BE5 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				2ED36BBC6E99E67A51AACA58 /* Pods_RunnerTests.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		33CC10EA2044A3C60003C045 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				29046788D63B65F2AC9DDCE8 /* Pods_Runner.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		331C80D6294CF71000263BE5 /* RunnerTests */ = {
+			isa = PBXGroup;
+			children = (
+				331C80D7294CF71000263BE5 /* RunnerTests.swift */,
+			);
+			path = RunnerTests;
+			sourceTree = "<group>";
+		};
+		33BA886A226E78AF003329D5 /* Configs */ = {
+			isa = PBXGroup;
+			children = (
+				33E5194F232828860026EE4D /* AppInfo.xcconfig */,
+				9740EEB21CF90195004384FC /* Debug.xcconfig */,
+				7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+				333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
+			);
+			path = Configs;
+			sourceTree = "<group>";
+		};
+		33CC10E42044A3C60003C045 = {
+			isa = PBXGroup;
+			children = (
+				33FAB671232836740065AC1E /* Runner */,
+				33CEB47122A05771004F2AC0 /* Flutter */,
+				331C80D6294CF71000263BE5 /* RunnerTests */,
+				33CC10EE2044A3C60003C045 /* Products */,
+				D73912EC22F37F3D000D13A0 /* Frameworks */,
+				C4A6A337FC30138A2A2BA9CA /* Pods */,
+			);
+			sourceTree = "<group>";
+		};
+		33CC10EE2044A3C60003C045 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				33CC10ED2044A3C60003C045 /* news_app.app */,
+				331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		33CC11242044D66E0003C045 /* Resources */ = {
+			isa = PBXGroup;
+			children = (
+				33CC10F22044A3C60003C045 /* Assets.xcassets */,
+				33CC10F42044A3C60003C045 /* MainMenu.xib */,
+				33CC10F72044A3C60003C045 /* Info.plist */,
+			);
+			name = Resources;
+			path = ..;
+			sourceTree = "<group>";
+		};
+		33CEB47122A05771004F2AC0 /* Flutter */ = {
+			isa = PBXGroup;
+			children = (
+				335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
+				33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
+				33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
+				33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
+			);
+			path = Flutter;
+			sourceTree = "<group>";
+		};
+		33FAB671232836740065AC1E /* Runner */ = {
+			isa = PBXGroup;
+			children = (
+				33CC10F02044A3C60003C045 /* AppDelegate.swift */,
+				33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
+				33E51913231747F40026EE4D /* DebugProfile.entitlements */,
+				33E51914231749380026EE4D /* Release.entitlements */,
+				33CC11242044D66E0003C045 /* Resources */,
+				33BA886A226E78AF003329D5 /* Configs */,
+			);
+			path = Runner;
+			sourceTree = "<group>";
+		};
+		C4A6A337FC30138A2A2BA9CA /* Pods */ = {
+			isa = PBXGroup;
+			children = (
+				C4497D3A28954422828E6444 /* Pods-Runner.debug.xcconfig */,
+				4B9071A6D9EBBE615E8B5867 /* Pods-Runner.release.xcconfig */,
+				FF3A7B2320378B0990411380 /* Pods-Runner.profile.xcconfig */,
+				F4E92A4EDEE5AF3A26B783CC /* Pods-RunnerTests.debug.xcconfig */,
+				11094761CF179F8D93B12C08 /* Pods-RunnerTests.release.xcconfig */,
+				1034281CAB2A67F991E61008 /* Pods-RunnerTests.profile.xcconfig */,
+			);
+			name = Pods;
+			path = Pods;
+			sourceTree = "<group>";
+		};
+		D73912EC22F37F3D000D13A0 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				A6694D74111009850CB5E4A1 /* Pods_Runner.framework */,
+				2F2F80C765F88DBFD9B91EC0 /* Pods_RunnerTests.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		331C80D4294CF70F00263BE5 /* RunnerTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+			buildPhases = (
+				4796E2C5BD1308DAF0C409DE /* [CP] Check Pods Manifest.lock */,
+				331C80D1294CF70F00263BE5 /* Sources */,
+				331C80D2294CF70F00263BE5 /* Frameworks */,
+				331C80D3294CF70F00263BE5 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				331C80DA294CF71000263BE5 /* PBXTargetDependency */,
+			);
+			name = RunnerTests;
+			productName = RunnerTests;
+			productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+		33CC10EC2044A3C60003C045 /* Runner */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
+			buildPhases = (
+				EE546ADE9467BD99665F770E /* [CP] Check Pods Manifest.lock */,
+				33CC10E92044A3C60003C045 /* Sources */,
+				33CC10EA2044A3C60003C045 /* Frameworks */,
+				33CC10EB2044A3C60003C045 /* Resources */,
+				33CC110E2044A8840003C045 /* Bundle Framework */,
+				3399D490228B24CF009A79C7 /* ShellScript */,
+				DF0F45C4E4EFE02947B0DEC1 /* [CP] Embed Pods Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				33CC11202044C79F0003C045 /* PBXTargetDependency */,
+			);
+			name = Runner;
+			productName = Runner;
+			productReference = 33CC10ED2044A3C60003C045 /* news_app.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		33CC10E52044A3C60003C045 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				BuildIndependentTargetsInParallel = YES;
+				LastSwiftUpdateCheck = 0920;
+				LastUpgradeCheck = 1510;
+				ORGANIZATIONNAME = "";
+				TargetAttributes = {
+					331C80D4294CF70F00263BE5 = {
+						CreatedOnToolsVersion = 14.0;
+						TestTargetID = 33CC10EC2044A3C60003C045;
+					};
+					33CC10EC2044A3C60003C045 = {
+						CreatedOnToolsVersion = 9.2;
+						LastSwiftMigration = 1100;
+						ProvisioningStyle = Automatic;
+						SystemCapabilities = {
+							com.apple.Sandbox = {
+								enabled = 1;
+							};
+						};
+					};
+					33CC111A2044C6BA0003C045 = {
+						CreatedOnToolsVersion = 9.2;
+						ProvisioningStyle = Manual;
+					};
+				};
+			};
+			buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
+			compatibilityVersion = "Xcode 9.3";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 33CC10E42044A3C60003C045;
+			productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				33CC10EC2044A3C60003C045 /* Runner */,
+				331C80D4294CF70F00263BE5 /* RunnerTests */,
+				33CC111A2044C6BA0003C045 /* Flutter Assemble */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		331C80D3294CF70F00263BE5 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		33CC10EB2044A3C60003C045 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
+				33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		3399D490228B24CF009A79C7 /* ShellScript */ = {
+			isa = PBXShellScriptBuildPhase;
+			alwaysOutOfDate = 1;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+			);
+			outputFileListPaths = (
+			);
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
+		};
+		33CC111E2044C6BF0003C045 /* ShellScript */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+				Flutter/ephemeral/FlutterInputs.xcfilelist,
+			);
+			inputPaths = (
+				Flutter/ephemeral/tripwire,
+			);
+			outputFileListPaths = (
+				Flutter/ephemeral/FlutterOutputs.xcfilelist,
+			);
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
+		};
+		4796E2C5BD1308DAF0C409DE /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		DF0F45C4E4EFE02947B0DEC1 /* [CP] Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+			);
+			name = "[CP] Embed Pods Frameworks";
+			outputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		EE546ADE9467BD99665F770E /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		331C80D1294CF70F00263BE5 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		33CC10E92044A3C60003C045 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
+				33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
+				335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 33CC10EC2044A3C60003C045 /* Runner */;
+			targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;
+		};
+		33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
+			targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+		33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
+			isa = PBXVariantGroup;
+			children = (
+				33CC10F52044A3C60003C045 /* Base */,
+			);
+			name = MainMenu.xib;
+			path = Runner;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		331C80DB294CF71000263BE5 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = F4E92A4EDEE5AF3A26B783CC /* Pods-RunnerTests.debug.xcconfig */;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CURRENT_PROJECT_VERSION = 1;
+				GENERATE_INFOPLIST_FILE = YES;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.xhxxf.newsApp.RunnerTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/news_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/news_app";
+			};
+			name = Debug;
+		};
+		331C80DC294CF71000263BE5 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 11094761CF179F8D93B12C08 /* Pods-RunnerTests.release.xcconfig */;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CURRENT_PROJECT_VERSION = 1;
+				GENERATE_INFOPLIST_FILE = YES;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.xhxxf.newsApp.RunnerTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/news_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/news_app";
+			};
+			name = Release;
+		};
+		331C80DD294CF71000263BE5 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 1034281CAB2A67F991E61008 /* Pods-RunnerTests.profile.xcconfig */;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CURRENT_PROJECT_VERSION = 1;
+				GENERATE_INFOPLIST_FILE = YES;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.xhxxf.newsApp.RunnerTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/news_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/news_app";
+			};
+			name = Profile;
+		};
+		338D0CE9231458BD00FA5F75 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CODE_SIGN_IDENTITY = "-";
+				COPY_PHASE_STRIP = NO;
+				DEAD_CODE_STRIPPING = YES;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_USER_SCRIPT_SANDBOXING = NO;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.15;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = macosx;
+				SWIFT_COMPILATION_MODE = wholemodule;
+				SWIFT_OPTIMIZATION_LEVEL = "-O";
+			};
+			name = Profile;
+		};
+		338D0CEA231458BD00FA5F75 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/../Frameworks",
+				);
+				PROVISIONING_PROFILE_SPECIFIER = "";
+				SWIFT_VERSION = 5.0;
+			};
+			name = Profile;
+		};
+		338D0CEB231458BD00FA5F75 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CODE_SIGN_STYLE = Manual;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Profile;
+		};
+		33CC10F92044A3C60003C045 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CODE_SIGN_IDENTITY = "-";
+				COPY_PHASE_STRIP = NO;
+				DEAD_CODE_STRIPPING = YES;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				ENABLE_USER_SCRIPT_SANDBOXING = NO;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.15;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = macosx;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+			};
+			name = Debug;
+		};
+		33CC10FA2044A3C60003C045 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CODE_SIGN_IDENTITY = "-";
+				COPY_PHASE_STRIP = NO;
+				DEAD_CODE_STRIPPING = YES;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_USER_SCRIPT_SANDBOXING = NO;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.15;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = macosx;
+				SWIFT_COMPILATION_MODE = wholemodule;
+				SWIFT_OPTIMIZATION_LEVEL = "-O";
+			};
+			name = Release;
+		};
+		33CC10FC2044A3C60003C045 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/../Frameworks",
+				);
+				PROVISIONING_PROFILE_SPECIFIER = "";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 5.0;
+			};
+			name = Debug;
+		};
+		33CC10FD2044A3C60003C045 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/../Frameworks",
+				);
+				PROVISIONING_PROFILE_SPECIFIER = "";
+				SWIFT_VERSION = 5.0;
+			};
+			name = Release;
+		};
+		33CC111C2044C6BA0003C045 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CODE_SIGN_STYLE = Manual;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		33CC111D2044C6BA0003C045 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CODE_SIGN_STYLE = Automatic;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				331C80DB294CF71000263BE5 /* Debug */,
+				331C80DC294CF71000263BE5 /* Release */,
+				331C80DD294CF71000263BE5 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				33CC10F92044A3C60003C045 /* Debug */,
+				33CC10FA2044A3C60003C045 /* Release */,
+				338D0CE9231458BD00FA5F75 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				33CC10FC2044A3C60003C045 /* Debug */,
+				33CC10FD2044A3C60003C045 /* Release */,
+				338D0CEA231458BD00FA5F75 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				33CC111C2044C6BA0003C045 /* Debug */,
+				33CC111D2044C6BA0003C045 /* Release */,
+				338D0CEB231458BD00FA5F75 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 33CC10E52044A3C60003C045 /* Project object */;
+}

+ 8 - 0
xinhuaribao/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>

+ 99 - 0
xinhuaribao/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme

@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1510"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+               BuildableName = "news_app.app"
+               BlueprintName = "Runner"
+               ReferencedContainer = "container:Runner.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+            BuildableName = "news_app.app"
+            BlueprintName = "Runner"
+            ReferencedContainer = "container:Runner.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <Testables>
+         <TestableReference
+            skipped = "NO"
+            parallelizable = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "331C80D4294CF70F00263BE5"
+               BuildableName = "RunnerTests.xctest"
+               BlueprintName = "RunnerTests"
+               ReferencedContainer = "container:Runner.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      enableGPUValidationMode = "1"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+            BuildableName = "news_app.app"
+            BlueprintName = "Runner"
+            ReferencedContainer = "container:Runner.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Profile"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+            BuildableName = "news_app.app"
+            BlueprintName = "Runner"
+            ReferencedContainer = "container:Runner.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 10 - 0
xinhuaribao/macos/Runner.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "group:Runner.xcodeproj">
+   </FileRef>
+   <FileRef
+      location = "group:Pods/Pods.xcodeproj">
+   </FileRef>
+</Workspace>

+ 8 - 0
xinhuaribao/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>

+ 13 - 0
xinhuaribao/macos/Runner/AppDelegate.swift

@@ -0,0 +1,13 @@
+import Cocoa
+import FlutterMacOS
+
+@main
+class AppDelegate: FlutterAppDelegate {
+  override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
+    return true
+  }
+
+  override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
+    return true
+  }
+}

+ 68 - 0
xinhuaribao/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json

@@ -0,0 +1,68 @@
+{
+  "images" : [
+    {
+      "size" : "16x16",
+      "idiom" : "mac",
+      "filename" : "app_icon_16.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "16x16",
+      "idiom" : "mac",
+      "filename" : "app_icon_32.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "32x32",
+      "idiom" : "mac",
+      "filename" : "app_icon_32.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "32x32",
+      "idiom" : "mac",
+      "filename" : "app_icon_64.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "128x128",
+      "idiom" : "mac",
+      "filename" : "app_icon_128.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "128x128",
+      "idiom" : "mac",
+      "filename" : "app_icon_256.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "256x256",
+      "idiom" : "mac",
+      "filename" : "app_icon_256.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "256x256",
+      "idiom" : "mac",
+      "filename" : "app_icon_512.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "512x512",
+      "idiom" : "mac",
+      "filename" : "app_icon_512.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "512x512",
+      "idiom" : "mac",
+      "filename" : "app_icon_1024.png",
+      "scale" : "2x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

BIN
xinhuaribao/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png


BIN
xinhuaribao/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png


BIN
xinhuaribao/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png


BIN
xinhuaribao/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png


BIN
xinhuaribao/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png


BIN
xinhuaribao/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png


BIN
xinhuaribao/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png


+ 343 - 0
xinhuaribao/macos/Runner/Base.lproj/MainMenu.xib

@@ -0,0 +1,343 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+    <dependencies>
+        <deployment identifier="macosx"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
+            <connections>
+                <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
+            </connections>
+        </customObject>
+        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+        <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Runner" customModuleProvider="target">
+            <connections>
+                <outlet property="applicationMenu" destination="uQy-DD-JDr" id="XBo-yE-nKs"/>
+                <outlet property="mainFlutterWindow" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
+            </connections>
+        </customObject>
+        <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
+        <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+            <items>
+                <menuItem title="APP_NAME" id="1Xt-HY-uBw">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="APP_NAME" systemMenu="apple" id="uQy-DD-JDr">
+                        <items>
+                            <menuItem title="About APP_NAME" id="5kV-Vb-QxS">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+                            <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
+                            <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+                            <menuItem title="Services" id="NMo-om-nkz">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+                            <menuItem title="Hide APP_NAME" keyEquivalent="h" id="Olw-nP-bQN">
+                                <connections>
+                                    <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Show All" id="Kd2-mp-pUS">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+                            <menuItem title="Quit APP_NAME" keyEquivalent="q" id="4sb-4s-VLi">
+                                <connections>
+                                    <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Edit" id="5QF-Oa-p0T">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Edit" id="W48-6f-4Dl">
+                        <items>
+                            <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
+                                <connections>
+                                    <action selector="undo:" target="-1" id="M6e-cu-g7V"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
+                                <connections>
+                                    <action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
+                            <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
+                                <connections>
+                                    <action selector="cut:" target="-1" id="YJe-68-I9s"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
+                                <connections>
+                                    <action selector="copy:" target="-1" id="G1f-GL-Joy"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
+                                <connections>
+                                    <action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Delete" id="pa3-QI-u2k">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
+                                <connections>
+                                    <action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
+                            <menuItem title="Find" id="4EN-yA-p0u">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Find" id="1b7-l0-nxx">
+                                    <items>
+                                        <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
+                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
+                                            <connections>
+                                                <action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
+                                    <items>
+                                        <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
+                                            <connections>
+                                                <action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
+                                            <connections>
+                                                <action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
+                                        <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Substitutions" id="9ic-FL-obx">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
+                                    <items>
+                                        <menuItem title="Show Substitutions" id="z6F-FW-3nz">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
+                                        <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Quotes" id="hQb-2v-fYv">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Dashes" id="rgM-f4-ycn">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Links" id="cwL-P1-jid">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Data Detectors" id="tRr-pd-1PS">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Text Replacement" id="HFQ-gK-NFA">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Transformations" id="2oI-Rn-ZJC">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
+                                    <items>
+                                        <menuItem title="Make Upper Case" id="vmV-6d-7jI">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Make Lower Case" id="d9M-CD-aMd">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Capitalize" id="UEZ-Bs-lqG">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Speech" id="xrE-MZ-jX0">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
+                                    <items>
+                                        <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="View" id="H8h-7b-M4v">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="View" id="HyV-fh-RgO">
+                        <items>
+                            <menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
+                                <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+                                <connections>
+                                    <action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Window" id="aUF-d1-5bR">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
+                        <items>
+                            <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
+                                <connections>
+                                    <action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Zoom" id="R4o-n2-Eq4">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
+                            <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Help" id="EPT-qC-fAb">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Help" systemMenu="help" id="rJ0-wn-3NY"/>
+                </menuItem>
+            </items>
+            <point key="canvasLocation" x="142" y="-258"/>
+        </menu>
+        <window title="APP_NAME" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="MainFlutterWindow" customModule="Runner" customModuleProvider="target">
+            <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
+            <rect key="contentRect" x="335" y="390" width="800" height="600"/>
+            <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
+            <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
+                <rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
+                <autoresizingMask key="autoresizingMask"/>
+            </view>
+        </window>
+    </objects>
+</document>

+ 14 - 0
xinhuaribao/macos/Runner/Configs/AppInfo.xcconfig

@@ -0,0 +1,14 @@
+// Application-level settings for the Runner target.
+//
+// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
+// future. If not, the values below would default to using the project name when this becomes a
+// 'flutter create' template.
+
+// The application's name. By default this is also the title of the Flutter window.
+PRODUCT_NAME = news_app
+
+// The application's bundle identifier
+PRODUCT_BUNDLE_IDENTIFIER = com.xhxxf.newsApp
+
+// The copyright displayed in application information
+PRODUCT_COPYRIGHT = Copyright © 2026 com.xhxxf. All rights reserved.

+ 2 - 0
xinhuaribao/macos/Runner/Configs/Debug.xcconfig

@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Debug.xcconfig"
+#include "Warnings.xcconfig"

+ 2 - 0
xinhuaribao/macos/Runner/Configs/Release.xcconfig

@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Release.xcconfig"
+#include "Warnings.xcconfig"

+ 13 - 0
xinhuaribao/macos/Runner/Configs/Warnings.xcconfig

@@ -0,0 +1,13 @@
+WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
+GCC_WARN_UNDECLARED_SELECTOR = YES
+CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
+CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
+CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
+CLANG_WARN_PRAGMA_PACK = YES
+CLANG_WARN_STRICT_PROTOTYPES = YES
+CLANG_WARN_COMMA = YES
+GCC_WARN_STRICT_SELECTOR_MATCH = YES
+CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
+CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
+GCC_WARN_SHADOW = YES
+CLANG_WARN_UNREACHABLE_CODE = YES

+ 12 - 0
xinhuaribao/macos/Runner/DebugProfile.entitlements

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>com.apple.security.app-sandbox</key>
+	<true/>
+	<key>com.apple.security.cs.allow-jit</key>
+	<true/>
+	<key>com.apple.security.network.server</key>
+	<true/>
+</dict>
+</plist>

+ 32 - 0
xinhuaribao/macos/Runner/Info.plist

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIconFile</key>
+	<string></string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>$(FLUTTER_BUILD_NAME)</string>
+	<key>CFBundleVersion</key>
+	<string>$(FLUTTER_BUILD_NUMBER)</string>
+	<key>LSMinimumSystemVersion</key>
+	<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
+	<key>NSHumanReadableCopyright</key>
+	<string>$(PRODUCT_COPYRIGHT)</string>
+	<key>NSMainNibFile</key>
+	<string>MainMenu</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>

+ 15 - 0
xinhuaribao/macos/Runner/MainFlutterWindow.swift

@@ -0,0 +1,15 @@
+import Cocoa
+import FlutterMacOS
+
+class MainFlutterWindow: NSWindow {
+  override func awakeFromNib() {
+    let flutterViewController = FlutterViewController()
+    let windowFrame = self.frame
+    self.contentViewController = flutterViewController
+    self.setFrame(windowFrame, display: true)
+
+    RegisterGeneratedPlugins(registry: flutterViewController)
+
+    super.awakeFromNib()
+  }
+}

+ 8 - 0
xinhuaribao/macos/Runner/Release.entitlements

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>com.apple.security.app-sandbox</key>
+	<true/>
+</dict>
+</plist>

+ 12 - 0
xinhuaribao/macos/RunnerTests/RunnerTests.swift

@@ -0,0 +1,12 @@
+import Cocoa
+import FlutterMacOS
+import XCTest
+
+class RunnerTests: XCTestCase {
+
+  func testExample() {
+    // If you add code to the Runner application, consider adding tests here.
+    // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
+  }
+
+}

+ 2 - 2
xinhuaribao/pubspec.lock

@@ -545,10 +545,10 @@ packages:
     dependency: "direct main"
     description:
       name: fluwx
-      sha256: "61e572248c38a775ad44731e2ec7c4b52cae673eda58222b5432ed721205471c"
+      sha256: "7e92d2000ee49c5262a88c51ea2d22b91a753d5b29df27cc264bb0a115d65373"
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "5.6.0"
+    version: "5.7.5"
   frontend_server_client:
     dependency: transitive
     description:

+ 1 - 1
xinhuaribao/pubspec.yaml

@@ -45,7 +45,7 @@ dependencies:
   shimmer: ^3.0.0
   connectivity_plus: ^6.1.4
   installed_apps: ^1.6.0
-  fluwx: ^5.6.0
+  fluwx: ^5.7.5
 
 dev_dependencies:
   flutter_test: