├── lib
├── pages
│ ├── message
│ │ ├── components
│ │ │ ├── vip.dart
│ │ │ ├── message_type.dart
│ │ │ ├── account_type_tag.dart
│ │ │ └── message_item.dart
│ │ └── message.dart
│ ├── user
│ │ └── user.dart
│ ├── home
│ │ ├── components
│ │ │ ├── hot_item.dart
│ │ │ ├── ask_menu.dart
│ │ │ ├── hot_choiceness.dart
│ │ │ ├── follows_recommend.dart
│ │ │ ├── ask_menu_item.dart
│ │ │ ├── user_item.dart
│ │ │ ├── choiceness_item.dart
│ │ │ ├── ask_item.dart
│ │ │ ├── hot_list.dart
│ │ │ └── speech_item.dart
│ │ ├── follows.dart
│ │ ├── job.dart
│ │ ├── recommend.dart
│ │ ├── hot.dart
│ │ ├── ask.dart
│ │ └── home.dart
│ ├── owner
│ │ ├── components
│ │ │ ├── owner_detail_line.dart
│ │ │ ├── owner_detail_item.dart
│ │ │ ├── owner_menu.dart
│ │ │ ├── owner_menu_item.dart
│ │ │ └── owner_info.dart
│ │ └── owner.dart
│ ├── post_detail
│ │ └── post_detail.dart
│ ├── contact
│ │ ├── components
│ │ │ ├── contact_menu.dart
│ │ │ ├── friend_msg_item.dart
│ │ │ ├── friend_msg.dart
│ │ │ ├── request_list.dart
│ │ │ ├── contact_item.dart
│ │ │ └── discover_more.dart
│ │ └── contact.dart
│ └── chance
│ │ ├── components
│ │ ├── company_list.dart
│ │ ├── follow_market.dart
│ │ ├── chance_notice.dart
│ │ └── post_item.dart
│ │ └── chance.dart
├── enums.dart
├── types.dart
├── 404.dart
├── components
│ ├── safe_area.dart
│ ├── no_more.dart
│ ├── vip_badge.dart
│ ├── tag.dart
│ ├── card_container.dart
│ ├── empty.dart
│ ├── app_bar_action.dart
│ ├── menu_item.dart
│ ├── badge.dart
│ ├── customer_tab_item.dart
│ ├── column_menu_item.dart
│ ├── app_ad.dart
│ ├── app_navbar.dart
│ ├── search_field.dart
│ └── navbar_item.dart
├── mock
│ ├── ad.dart
│ ├── message.dart
│ ├── contact.dart
│ ├── company.dart
│ └── home.dart
├── common
│ ├── common.dart
│ └── customer_tab_size_indicator.dart
├── router.dart
├── constants.dart
├── main.dart
└── main copy.dart
├── ios
├── Runner
│ ├── Runner-Bridging-Header.h
│ ├── Assets.xcassets
│ │ ├── LaunchImage.imageset
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ ├── README.md
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-83.5x83.5@2x.png
│ │ │ └── Contents.json
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── Main.storyboard
│ │ └── LaunchScreen.storyboard
│ └── Info.plist
├── Flutter
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── AppFrameworkInfo.plist
├── Runner.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
├── .gitignore
└── Podfile
├── assets
├── icons
│ └── vip.png
├── fonts
│ └── iconfont.ttf
└── images
│ ├── avatar.png
│ └── empty.png
├── android
├── gradle.properties
├── app
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── drawable
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable-v21
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── values
│ │ │ │ │ └── styles.xml
│ │ │ │ └── values-night
│ │ │ │ │ └── styles.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── flutter_maimai
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ └── profile
│ │ │ └── AndroidManifest.xml
│ └── build.gradle
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── .gitignore
├── settings.gradle
└── build.gradle
├── .metadata
├── README.md
├── .gitignore
├── test
└── widget_test.dart
├── pubspec.yaml
└── pubspec.lock
/lib/pages/message/components/vip.dart:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/lib/enums.dart:
--------------------------------------------------------------------------------
1 | enum NavbarMessageType {
2 | dot,
3 | number,
4 | }
5 |
--------------------------------------------------------------------------------
/assets/icons/vip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/assets/icons/vip.png
--------------------------------------------------------------------------------
/assets/fonts/iconfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/assets/fonts/iconfont.ttf
--------------------------------------------------------------------------------
/assets/images/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/assets/images/avatar.png
--------------------------------------------------------------------------------
/assets/images/empty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/assets/images/empty.png
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BingKui/flutter_maimai/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/flutter_maimai/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.flutter_maimai
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/lib/types.dart:
--------------------------------------------------------------------------------
1 | // 帖子详情参数
2 | class PostDetailParam {
3 | final int postId;
4 | PostDetailParam(this.postId);
5 | }
6 |
7 | // 用户详情参数
8 | class UserDetailParam {
9 | final int userId;
10 | UserDetailParam(this.userId);
11 | }
12 |
--------------------------------------------------------------------------------
/lib/404.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class Error extends StatelessWidget {
4 | const Error({Key? key}) : super(key: key);
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return Container();
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/lib/pages/user/user.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class User extends StatelessWidget {
4 | const User({Key? key}) : super(key: key);
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return Container();
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
7 |
--------------------------------------------------------------------------------
/lib/pages/home/components/hot_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class HotItem extends StatelessWidget {
4 | const HotItem({Key? key}) : super(key: key);
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return Container();
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: f4abaa0735eba4dfd8f33f73363911d63931fe03
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/lib/components/safe_area.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class SafeAreaBottom extends StatelessWidget {
4 | const SafeAreaBottom({Key? key}) : super(key: key);
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return Container(
9 | height: 200,
10 | color: Colors.transparent,
11 | );
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/lib/pages/owner/components/owner_detail_line.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class OwnerDetailLine extends StatelessWidget {
4 | const OwnerDetailLine({Key? key}) : super(key: key);
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return Container(
9 | width: 1,
10 | height: 50,
11 | color: Colors.black12,
12 | );
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/lib/mock/ad.dart:
--------------------------------------------------------------------------------
1 | const String ChanceImageAdUrl =
2 | 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2F4b77dd153586a7139d5c1cf610fc2bc177346d3641059-6NW7Xk_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1631703668&t=b8605ed942ec1f53eacb3523c74fa65d';
3 |
4 | const String AskImageAdUrl = 'https://img0.baidu.com/it/u=512655854,4064430547&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500';
5 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/components/no_more.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 |
4 | class NoMore extends StatelessWidget {
5 | const NoMore({
6 | Key? key,
7 | this.tip = '没有更多了',
8 | }) : super(key: key);
9 |
10 | final String tip;
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | return Container(
15 | padding: EdgeInsets.all(iDefaultPadding),
16 | child: Text(
17 | tip,
18 | style: TextStyle(fontSize: 16, color: Colors.black45),
19 | ),
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # flutter_maimai
2 |
3 | A new Flutter project.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13 |
14 | For help getting started with Flutter, view our
15 | [online documentation](https://flutter.dev/docs), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/lib/pages/post_detail/post_detail.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/types.dart';
3 |
4 | class PostDetail extends StatefulWidget {
5 | const PostDetail({Key? key}) : super(key: key);
6 | @override
7 | _PostDetailState createState() => _PostDetailState();
8 | }
9 |
10 | class _PostDetailState extends State {
11 | @override
12 | Widget build(BuildContext context) {
13 | final args = ModalRoute.of(context)!.settings.arguments as PostDetailParam;
14 | return Scaffold(
15 | appBar: AppBar(),
16 | body: Text('传过来的ID为${args.postId}'),
17 | );
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.50'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:4.1.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | project.evaluationDependsOn(':app')
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/ephemeral/
22 | Flutter/app.flx
23 | Flutter/app.zip
24 | Flutter/flutter_assets/
25 | Flutter/flutter_export_environment.sh
26 | ServiceDefinitions.json
27 | Runner/GeneratedPluginRegistrant.*
28 |
29 | # Exceptions to above rules.
30 | !default.mode1v3
31 | !default.mode2v3
32 | !default.pbxuser
33 | !default.perspectivev3
34 |
--------------------------------------------------------------------------------
/lib/components/vip_badge.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 |
4 | class VipBadge extends StatelessWidget {
5 | const VipBadge({Key? key}) : super(key: key);
6 |
7 | @override
8 | Widget build(BuildContext context) {
9 | return Container(
10 | width: 14,
11 | height: 14,
12 | // padding: EdgeInsets.symmetric(horizontal: 4, vertical: 4),
13 | decoration: BoxDecoration(
14 | color: iPrimaryColor.withOpacity(0.4),
15 | borderRadius: BorderRadius.all(Radius.circular(4)),
16 | ),
17 | child: Center(
18 | child: Image.asset(
19 | 'assets/icons/vip.png',
20 | width: 10,
21 | height: 10,
22 | ),
23 | ),
24 | );
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib/pages/home/follows.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/empty.dart';
3 | import 'package:flutter_maimai/components/no_more.dart';
4 | import 'package:flutter_maimai/components/safe_area.dart';
5 | import 'package:flutter_maimai/pages/home/components/follows_recommend.dart';
6 |
7 | class Follows extends StatelessWidget {
8 | const Follows({Key? key}) : super(key: key);
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return SingleChildScrollView(
13 | child: Column(
14 | children: [
15 | Empty(
16 | title: '还没有关注任何人呢',
17 | tip: '关注你感兴趣的人,他们发布的内容会在这里展示',
18 | ),
19 | FollowsRecommend(),
20 | NoMore(),
21 | SafeAreaBottom(),
22 | ],
23 | ),
24 | );
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib/pages/owner/components/owner_detail_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class OwnerDetailItem extends StatelessWidget {
4 | const OwnerDetailItem({
5 | Key? key,
6 | required this.label,
7 | required this.value,
8 | }) : super(key: key);
9 |
10 | final String label;
11 | final int value;
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return Expanded(
16 | child: Column(
17 | children: [
18 | Text(
19 | '$value',
20 | style: TextStyle(fontSize: 22, color: Colors.black, fontWeight: FontWeight.bold),
21 | ),
22 | SizedBox(height: 5),
23 | Text(
24 | label,
25 | style: TextStyle(color: Colors.black38, fontSize: 14),
26 | ),
27 | ],
28 | ),
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib/pages/home/job.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 | import 'package:flutter_maimai/mock/home.dart';
4 | import 'package:flutter_maimai/pages/home/components/speech_item.dart';
5 |
6 | class Job extends StatefulWidget {
7 | const Job({Key? key}) : super(key: key);
8 |
9 | @override
10 | _JobState createState() => _JobState();
11 | }
12 |
13 | class _JobState extends State {
14 | List speechList = SpeechMockList;
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return Container(
19 | padding: EdgeInsets.only(top: iDefaultPadding / 2),
20 | child: ListView.builder(
21 | itemBuilder: (context, index) {
22 | return SpeechItem(speech: speechList[index]);
23 | },
24 | itemCount: speechList.length,
25 | ),
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 11.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/common/common.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 | import 'package:flutter/material.dart';
3 |
4 | class CustomSliverDelegate extends SliverPersistentHeaderDelegate {
5 | CustomSliverDelegate({
6 | required this.minHeight,
7 | required this.maxHeight,
8 | required this.child,
9 | });
10 |
11 | final double minHeight;
12 | final double maxHeight;
13 | final Widget child;
14 |
15 | @override
16 | double get minExtent => minHeight;
17 |
18 | @override
19 | double get maxExtent => max(maxHeight, minHeight);
20 |
21 | @override
22 | Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
23 | return new SizedBox.expand(child: child);
24 | }
25 |
26 | @override
27 | bool shouldRebuild(CustomSliverDelegate oldDelegate) {
28 | return maxHeight != oldDelegate.maxHeight || minHeight != oldDelegate.minHeight || child != oldDelegate.child;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Android Studio will place build artifacts here
44 | /android/app/debug
45 | /android/app/profile
46 | /android/app/release
47 |
--------------------------------------------------------------------------------
/lib/components/tag.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 |
4 | class Tag extends StatelessWidget {
5 | const Tag(this.value, {Key? key, this.plain = false}) : super(key: key);
6 |
7 | final String value;
8 | final bool plain;
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return Container(
13 | padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
14 | decoration: BoxDecoration(
15 | color: plain ? Colors.transparent : iTagBackgroundColor,
16 | borderRadius: BorderRadius.all(Radius.circular(4)),
17 | border: new Border.all(width: 1, color: iTagBackgroundColor),
18 | ),
19 | child: Text(
20 | value,
21 | style: TextStyle(
22 | color: iTagTextColor,
23 | fontSize: 12,
24 | fontWeight: FontWeight.w500,
25 | ),
26 | ),
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/pages/contact/components/contact_menu.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/card_container.dart';
3 | import 'package:flutter_maimai/components/menu_item.dart';
4 | import 'package:flutter_maimai/components/self_icon.dart';
5 |
6 | class ContactMenu extends StatelessWidget {
7 | const ContactMenu({Key? key}) : super(key: key);
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return CardContainer(
12 | child: Row(
13 | mainAxisAlignment: MainAxisAlignment.spaceAround,
14 | children: [
15 | MenuItem(name: '我的好友', press: () {}, icon: SelfIcon.ContactFilling),
16 | MenuItem(name: '人脉探索', press: () {}, icon: SelfIcon.Search),
17 | MenuItem(name: '校友群', press: () {}, icon: SelfIcon.Layers),
18 | MenuItem(name: '极速找人', press: () {}, icon: SelfIcon.Operation),
19 | ],
20 | ),
21 | );
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/pages/home/recommend.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 | import 'package:flutter_maimai/mock/home.dart';
4 | import 'package:flutter_maimai/pages/home/components/speech_item.dart';
5 |
6 | class Recommend extends StatefulWidget {
7 | const Recommend({Key? key}) : super(key: key);
8 |
9 | @override
10 | _RecommendState createState() => _RecommendState();
11 | }
12 |
13 | class _RecommendState extends State {
14 | List speechList = SpeechMockList;
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return Container(
19 | padding: EdgeInsets.only(top: iDefaultPadding / 2, bottom: iDefaultPadding * 4),
20 | child: ListView.builder(
21 | itemBuilder: (context, index) {
22 | return SpeechItem(speech: speechList[index]);
23 | },
24 | itemCount: speechList.length,
25 | ),
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/lib/components/card_container.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 |
4 | const EdgeInsets _margin = EdgeInsets.only(
5 | left: iDefaultPadding,
6 | right: iDefaultPadding,
7 | bottom: iDefaultPadding,
8 | );
9 |
10 | const EdgeInsets _padding = EdgeInsets.symmetric(
11 | vertical: iDefaultPadding,
12 | );
13 |
14 | class CardContainer extends StatelessWidget {
15 | const CardContainer({
16 | Key? key,
17 | this.child,
18 | this.margin = _margin,
19 | this.padding = _padding,
20 | this.height,
21 | }) : super(key: key);
22 |
23 | final Widget? child;
24 | final double? height;
25 | final EdgeInsets margin, padding;
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | return Container(
30 | margin: margin,
31 | padding: padding,
32 | height: height,
33 | decoration: BoxDecoration(
34 | color: Colors.white,
35 | borderRadius: iBorderRadius,
36 | ),
37 | child: child,
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/lib/pages/message/components/message_type.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 |
4 | class MessageType extends StatelessWidget {
5 | const MessageType({
6 | Key? key,
7 | required this.text,
8 | required this.press,
9 | }) : super(key: key);
10 |
11 | final String text;
12 | final VoidCallback press;
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return GestureDetector(
17 | onTap: press,
18 | child: Container(
19 | height: 35,
20 | margin: EdgeInsets.only(right: iDefaultPadding / 2),
21 | padding: EdgeInsets.symmetric(horizontal: iDefaultPadding / 2),
22 | decoration: BoxDecoration(
23 | color: Colors.white,
24 | borderRadius: BorderRadius.all(Radius.circular(35)),
25 | ),
26 | child: Center(
27 | child: Text(
28 | text,
29 | style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: iTipColor),
30 | )),
31 | ),
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/lib/pages/message/components/account_type_tag.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 |
4 | class AccountTypeTag extends StatelessWidget {
5 | const AccountTypeTag({
6 | Key? key,
7 | this.isOfficial = false,
8 | this.isCompany = false,
9 | }) : super(key: key);
10 |
11 | final bool isOfficial, isCompany;
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | bool _isNoEmpty = isCompany || isOfficial;
16 | String _tagValue = isCompany ? '企业' : (isOfficial ? '官方' : '');
17 | return _isNoEmpty
18 | ? Container(
19 | margin: EdgeInsets.only(left: iDefaultPadding / 2),
20 | padding: EdgeInsets.symmetric(horizontal: 5, vertical: 1),
21 | decoration: BoxDecoration(
22 | border: Border.all(color: Colors.black12, width: 1),
23 | borderRadius: BorderRadius.all(Radius.circular(4)),
24 | ),
25 | child: Text(
26 | _tagValue,
27 | style: TextStyle(fontSize: 12, color: Colors.black26),
28 | ),
29 | )
30 | : Text('');
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/pages/home/hot.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/pages/home/components/hot_choiceness.dart';
3 | import 'package:flutter_maimai/pages/home/components/hot_list.dart';
4 | import 'package:flutter_maimai/mock/home.dart';
5 | import 'package:flutter_maimai/pages/home/components/speech_item.dart';
6 |
7 | class Hot extends StatelessWidget {
8 | const Hot({Key? key}) : super(key: key);
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | List speechList = SpeechMockList;
13 | return Container(
14 | color: Colors.white,
15 | child: ListView.builder(
16 | shrinkWrap: true,
17 | itemBuilder: (context, index) {
18 | List _list = [];
19 | if (index == 0) {
20 | _list.add(HotList());
21 | _list.add(HotChoiceness());
22 | _list.add(SpeechItem(speech: speechList[index]));
23 | return Column(
24 | children: _list,
25 | );
26 | }
27 | return SpeechItem(speech: speechList[index]);
28 | },
29 | itemCount: speechList.length,
30 | ),
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:flutter_maimai/main.dart';
12 |
13 | void main() {
14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(MyApp());
17 |
18 | // Verify that our counter starts at 0.
19 | expect(find.text('0'), findsOneWidget);
20 | expect(find.text('1'), findsNothing);
21 |
22 | // Tap the '+' icon and trigger a frame.
23 | await tester.tap(find.byIcon(Icons.add));
24 | await tester.pump();
25 |
26 | // Verify that our counter has incremented.
27 | expect(find.text('0'), findsNothing);
28 | expect(find.text('1'), findsOneWidget);
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/lib/components/empty.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 |
4 | class Empty extends StatelessWidget {
5 | const Empty({
6 | Key? key,
7 | required this.title,
8 | this.tip = '',
9 | }) : super(key: key);
10 |
11 | final String title, tip;
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | Size size = MediaQuery.of(context).size;
16 | return Container(
17 | width: size.width * 0.8,
18 | margin: EdgeInsets.all(iDefaultPadding),
19 | child: Column(
20 | mainAxisAlignment: MainAxisAlignment.center,
21 | crossAxisAlignment: CrossAxisAlignment.center,
22 | children: [
23 | Image.asset(
24 | 'assets/images/empty.png',
25 | width: 160,
26 | height: 160,
27 | ),
28 | Padding(
29 | padding: EdgeInsets.only(top: iDefaultPadding, bottom: iDefaultPadding / 2),
30 | child: Text(title, style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500, color: Colors.black)),
31 | ),
32 | Text(tip, style: TextStyle(fontSize: 14, color: Colors.black54)),
33 | ],
34 | ),
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib/pages/contact/components/friend_msg_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 |
4 | class FriendMsgItem extends StatelessWidget {
5 | const FriendMsgItem({
6 | Key? key,
7 | required this.message,
8 | required this.press,
9 | }) : super(key: key);
10 |
11 | final message;
12 | final VoidCallback press;
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return Container(
17 | height: 30,
18 | margin: EdgeInsets.only(bottom: iDefaultPadding / 2),
19 | child: Row(
20 | children: [
21 | // Text('12123'),
22 | CircleAvatar(
23 | radius: 15,
24 | backgroundImage: NetworkImage(message['avatar']),
25 | ),
26 | SizedBox(width: iDefaultPadding / 2),
27 | Text(
28 | message['nickname'] + ':',
29 | style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500, color: Colors.black),
30 | ),
31 | Expanded(
32 | child: Text(
33 | message['tip'],
34 | style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
35 | ),
36 | ),
37 | ],
38 | ),
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/components/app_bar_action.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 |
4 | const double actionSize = 40.0;
5 |
6 | class AppBarAction extends StatelessWidget {
7 | const AppBarAction({
8 | Key? key,
9 | required this.icon,
10 | required this.press,
11 | this.left = 0,
12 | this.right = 0,
13 | }) : super(key: key);
14 |
15 | final IconData icon;
16 | final VoidCallback press;
17 | final double left, right;
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | return Stack(
22 | alignment: AlignmentDirectional.center,
23 | children: [
24 | Container(
25 | width: actionSize,
26 | height: actionSize,
27 | margin: EdgeInsets.only(left: left, right: right),
28 | decoration: BoxDecoration(
29 | color: Colors.white,
30 | borderRadius: BorderRadius.all(Radius.circular(actionSize)),
31 | ),
32 | child: GestureDetector(
33 | onTap: press,
34 | child: Center(
35 | child: Icon(
36 | icon,
37 | color: Colors.black,
38 | ),
39 | ),
40 | ),
41 | ),
42 | ],
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/lib/router.dart:
--------------------------------------------------------------------------------
1 | // 路由配置
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_maimai/404.dart';
4 |
5 | // 路由配置信息
6 | import 'package:flutter_maimai/pages/post_detail/post_detail.dart';
7 | import 'package:flutter_maimai/pages/user/user.dart';
8 |
9 | final Map iPageRouter = {
10 | '/postDetail': (context) => PostDetail(),
11 | '/user': (context) => User(),
12 | };
13 |
14 | // Route iPageRouterController(RouteSettings settings) {
15 | // if (settings.name == )
16 | // }
17 |
18 | // 路由传参配置方法
19 | // Route pageRouterController(RouteSettings settings) {
20 | // final name = settings.name;
21 | // final Function? pageContentBuilder = iPageRouter[name];
22 | // print('运行到这里');
23 | // print(pageContentBuilder);
24 | // if (pageContentBuilder != null) {
25 | // if (settings.arguments != null) {
26 | // print('运行到这里');
27 |
28 | // final Route route = MaterialPageRoute(builder: (context) => pageContentBuilder(context, arguments: settings.arguments));
29 | // return route;
30 | // } else {
31 | // final Route route = MaterialPageRoute(builder: (context) => pageContentBuilder(context));
32 | // return route;
33 | // }
34 | // } else {
35 | // return MaterialPageRoute(builder: (context) => Error());
36 | // }
37 | // }
38 |
--------------------------------------------------------------------------------
/lib/pages/owner/components/owner_menu.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/self_icon.dart';
3 | import 'package:flutter_maimai/constants.dart';
4 | import 'package:flutter_maimai/pages/owner/components/owner_menu_item.dart';
5 |
6 | class OwnerMenu extends StatelessWidget {
7 | const OwnerMenu({Key? key}) : super(key: key);
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return Container(
12 | margin: EdgeInsets.only(bottom: iDefaultPadding, left: iDefaultPadding, right: iDefaultPadding),
13 | child: Row(
14 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
15 | children: [
16 | OwnerMenuItem(
17 | icon: SelfIcon.FavoriteFilling,
18 | name: '我的圈子',
19 | press: () {},
20 | ),
21 | OwnerMenuItem(
22 | icon: SelfIcon.FileCommonFilling,
23 | name: '我的关注',
24 | press: () {},
25 | ),
26 | OwnerMenuItem(
27 | icon: SelfIcon.ClockFilling,
28 | name: '最近浏览',
29 | press: () {},
30 | ),
31 | OwnerMenuItem(
32 | icon: SelfIcon.CommentFilling,
33 | name: '我的互动',
34 | press: () {},
35 | ),
36 | ],
37 | ),
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/lib/components/menu_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 |
4 | class MenuItem extends StatelessWidget {
5 | const MenuItem({
6 | Key? key,
7 | required this.name,
8 | required this.press,
9 | required this.icon,
10 | }) : super(key: key);
11 |
12 | final String name;
13 | final VoidCallback press;
14 | final IconData icon;
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return GestureDetector(
19 | onTap: press,
20 | child: Column(
21 | mainAxisAlignment: MainAxisAlignment.center,
22 | crossAxisAlignment: CrossAxisAlignment.center,
23 | children: [
24 | Container(
25 | height: iFollowMenuHeight,
26 | width: iFollowMenuHeight,
27 | margin: EdgeInsets.only(bottom: iDefaultPadding / 2),
28 | decoration: BoxDecoration(
29 | color: Color(0xFFF9FAFC),
30 | borderRadius: BorderRadius.all(Radius.circular(iFollowMenuHeight)),
31 | ),
32 | child: Icon(icon),
33 | ),
34 | Text(
35 | name,
36 | style: TextStyle(
37 | fontSize: 14,
38 | fontWeight: FontWeight.w500,
39 | ),
40 | )
41 | ],
42 | ),
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/lib/pages/owner/components/owner_menu_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 |
4 | class OwnerMenuItem extends StatelessWidget {
5 | const OwnerMenuItem({
6 | Key? key,
7 | required this.icon,
8 | required this.name,
9 | required this.press,
10 | }) : super(key: key);
11 |
12 | final IconData icon;
13 | final String name;
14 | final VoidCallback press;
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | Size size = MediaQuery.of(context).size;
19 | double _width = (size.width - iDefaultPadding * 2 - 30) / 4;
20 | return Container(
21 | width: _width,
22 | height: _width,
23 | decoration: BoxDecoration(
24 | color: Colors.white,
25 | borderRadius: iBorderRadius,
26 | ),
27 | child: Stack(
28 | children: [
29 | Positioned(
30 | left: iDefaultPadding / 2,
31 | top: iDefaultPadding / 2,
32 | child: Icon(
33 | icon,
34 | size: 26,
35 | ),
36 | ),
37 | Positioned(
38 | bottom: iDefaultPadding / 2,
39 | left: 0,
40 | right: 0,
41 | child: Center(
42 | child: Text(name, style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500)),
43 | ),
44 | )
45 | ],
46 | ),
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/lib/constants.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | // 定义基本颜色
4 | const iAppBackgroundColor = Color(0xFFF4F5FA);
5 | const iAppBarBackgroundColor = Color(0xFFF4F5FA);
6 |
7 | // 定义在 app 中用到的颜色
8 | const iPrimaryColor = Color(0xFF2150F4); // 主题色
9 | const iTextColor = Color(0xFF3C4046);
10 | const iTipColor = Color(0xFF7C7D81);
11 | const iBackgroundColor = Color(0xFFF9F8FD);
12 | const iAppBottomBarColor = Color(0xFF555763);
13 | const iTagBackgroundColor = Color(0xFFF3F4FA);
14 | const iTagTextColor = Color(0xFF7B7D84);
15 | // 定义边界padding
16 | const double iDefaultPadding = 20.0;
17 | const BorderRadiusGeometry iBorderRadius = BorderRadius.all(Radius.circular(10));
18 |
19 | // 定义基本样式变量
20 | const List iBoxShadow = [
21 | BoxShadow(
22 | color: Colors.black12,
23 | offset: Offset(0, 0), //阴影y轴偏移量
24 | blurRadius: 5, //阴影模糊程度
25 | spreadRadius: 5, //阴影扩散程度
26 | ),
27 | ];
28 |
29 | // 定义基本组件的尺寸
30 | const double iSearchHeight = 54.0;
31 | const double iModalHeaderHeight = 65.0;
32 | const double iColumnMenuHeight = 60.0;
33 |
34 | // appbar
35 | const TextStyle iAppBarTextStyle = TextStyle(
36 | color: Colors.black,
37 | fontSize: 24,
38 | fontWeight: FontWeight.bold,
39 | );
40 |
41 | // app-navbar
42 | const double iNavBarHeight = 65.0;
43 | const double iNavBarIconSize = 40.0;
44 | const iNavbarColor = Color(0xFFAAACB8);
45 | const iNavbarActiveColor = Colors.white;
46 |
47 | // chance page
48 | const double iFollowMenuHeight = 50.0;
49 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '11.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/lib/components/badge.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 |
4 | const double badgeSize = 20.0;
5 | const double badgeDotSize = 10.0;
6 |
7 | class SelfBadge extends StatelessWidget {
8 | const SelfBadge(
9 | this.value, {
10 | Key? key,
11 | this.right,
12 | this.top,
13 | this.left,
14 | this.bottom,
15 | this.color = Colors.red,
16 | this.dot = false,
17 | this.fixed = true,
18 | }) : super(key: key);
19 |
20 | final double? right, top, left, bottom;
21 | final bool dot, fixed;
22 | final Color? color;
23 | final String value;
24 |
25 | Widget _buildContent() {
26 | TextStyle _style = TextStyle(fontSize: 12, color: Colors.white);
27 | return Container(
28 | padding: EdgeInsets.symmetric(horizontal: 5),
29 | height: dot ? badgeDotSize : badgeSize,
30 | decoration: BoxDecoration(
31 | color: color,
32 | borderRadius: BorderRadius.all(Radius.circular(dot ? badgeDotSize : badgeSize)),
33 | ),
34 | child: dot
35 | ? Text('')
36 | : Align(
37 | child: Text(value, style: _style),
38 | ),
39 | );
40 | }
41 |
42 | @override
43 | Widget build(BuildContext context) {
44 | return fixed
45 | ? Positioned(
46 | left: left,
47 | right: right,
48 | top: top,
49 | bottom: bottom,
50 | child: _buildContent(),
51 | )
52 | : _buildContent();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/pages/home/components/ask_menu.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/self_icon.dart';
3 | import 'package:flutter_maimai/constants.dart';
4 | import 'package:flutter_maimai/pages/home/components/ask_menu_item.dart';
5 |
6 | class AskMenu extends StatelessWidget {
7 | const AskMenu({Key? key}) : super(key: key);
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return Container(
12 | margin: EdgeInsets.all(iDefaultPadding / 2),
13 | child: Column(
14 | children: [
15 | AskMenuItem(
16 | title: '去发现感兴趣的公司',
17 | desc: '暂无关注的公司',
18 | icon: SelfIcon.Home,
19 | isLink: true,
20 | press: () {},
21 | ),
22 | SizedBox(height: iDefaultPadding / 2),
23 | Row(
24 | children: [
25 | Expanded(
26 | child: AskMenuItem(
27 | title: '大厂职级揭秘',
28 | desc: '洞察大环境',
29 | icon: SelfIcon.Home,
30 | press: () {},
31 | ),
32 | ),
33 | SizedBox(width: iDefaultPadding / 2),
34 | Expanded(
35 | child: AskMenuItem(
36 | title: '员工认可榜单',
37 | desc: '甄选好机遇',
38 | icon: SelfIcon.Home,
39 | press: () {},
40 | ),
41 | ),
42 | ],
43 | ),
44 | ],
45 | ),
46 | );
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib/pages/home/components/hot_choiceness.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 | import 'package:flutter_maimai/mock/home.dart';
4 | import 'package:flutter_maimai/pages/home/components/choiceness_item.dart';
5 |
6 | class HotChoiceness extends StatefulWidget {
7 | const HotChoiceness({Key? key}) : super(key: key);
8 |
9 | @override
10 | _HotChoicenessState createState() => _HotChoicenessState();
11 | }
12 |
13 | class _HotChoicenessState extends State {
14 | List columnList = HotChoicenessColumnMock;
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | Size size = MediaQuery.of(context).size;
19 | return Container(
20 | width: size.width,
21 | height: 160,
22 | margin: EdgeInsets.only(left: iDefaultPadding),
23 | child: Column(
24 | crossAxisAlignment: CrossAxisAlignment.start,
25 | children: [
26 | Padding(
27 | padding: EdgeInsets.only(bottom: iDefaultPadding / 2),
28 | child: Text(
29 | '精选专栏',
30 | style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
31 | ),
32 | ),
33 | Container(
34 | height: 120,
35 | child: ListView.builder(
36 | shrinkWrap: true,
37 | scrollDirection: Axis.horizontal,
38 | itemBuilder: (context, index) {
39 | return ChoicenessItem(item: columnList[index]);
40 | },
41 | itemCount: columnList.length,
42 | ),
43 | ),
44 | ],
45 | ),
46 | );
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib/pages/home/ask.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/app_ad.dart';
3 | import 'package:flutter_maimai/components/safe_area.dart';
4 | import 'package:flutter_maimai/constants.dart';
5 | import 'package:flutter_maimai/mock/ad.dart';
6 | import 'package:flutter_maimai/mock/home.dart';
7 | import 'package:flutter_maimai/pages/home/components/ask_item.dart';
8 | import 'package:flutter_maimai/pages/home/components/ask_menu.dart';
9 |
10 | class Ask extends StatefulWidget {
11 | const Ask({Key? key}) : super(key: key);
12 |
13 | @override
14 | _AskState createState() => _AskState();
15 | }
16 |
17 | class _AskState extends State {
18 | List askList = AskMockList;
19 | @override
20 | Widget build(BuildContext context) {
21 | Size size = MediaQuery.of(context).size;
22 | return Container(
23 | height: size.height,
24 | child: ListView.builder(
25 | shrinkWrap: true,
26 | itemBuilder: (context, index) {
27 | if (index == 0) {
28 | return AskMenu();
29 | }
30 | if (index == 1) {
31 | return ImageAd(
32 | AskImageAdUrl,
33 | margin: EdgeInsets.only(
34 | left: iDefaultPadding / 2,
35 | right: iDefaultPadding / 2,
36 | bottom: iDefaultPadding / 2,
37 | ),
38 | );
39 | }
40 | if (index == askList.length + 2) {
41 | return SafeAreaBottom();
42 | }
43 | return AskItem(askList[index - 2]);
44 | },
45 | itemCount: askList.length + 3,
46 | ),
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/components/customer_tab_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/badge.dart';
3 |
4 | class CustomerTabItem extends StatelessWidget {
5 | const CustomerTabItem({
6 | Key? key,
7 | required this.title,
8 | required this.active,
9 | required this.press,
10 | this.badge = 'new',
11 | this.haveBadge = false,
12 | this.haveBorder = false,
13 | this.isScale = false,
14 | }) : super(key: key);
15 |
16 | final String title, badge;
17 | final bool active, haveBadge, haveBorder, isScale;
18 | final VoidCallback press;
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | TextStyle _style = TextStyle(
23 | fontSize: isScale ? (active ? 20 : 18) : 18,
24 | color: active ? Colors.black : Colors.black38,
25 | fontWeight: FontWeight.w500,
26 | );
27 | return GestureDetector(
28 | onTap: press,
29 | child: Container(
30 | height: 30,
31 | child: Stack(
32 | clipBehavior: Clip.none,
33 | children: [
34 | haveBorder
35 | ? Column(
36 | children: [
37 | Text(title, style: _style),
38 | Container(
39 | height: 2,
40 | width: 20,
41 | decoration: BoxDecoration(
42 | color: active ? Colors.black : Colors.transparent,
43 | ),
44 | ),
45 | ],
46 | )
47 | : Text(title, style: _style),
48 | haveBadge ? SelfBadge(badge, top: -8, right: -30) : Text(''),
49 | ],
50 | ),
51 | ),
52 | );
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/pages/chance/components/company_list.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | class CompanyList extends StatelessWidget {
6 | const CompanyList({Key? key}) : super(key: key);
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return Container(
11 | child: Column(
12 | children: [
13 | SliverPersistentHeader(
14 | pinned: true, //是否固定在顶部
15 | floating: true,
16 | delegate: _SliverAppBarDelegate(
17 | minHeight: 50, //收起的高度
18 | maxHeight: 50, //展开的最大高度
19 | child: Container(
20 | padding: EdgeInsets.only(left: 16),
21 | color: Colors.pink,
22 | alignment: Alignment.centerLeft,
23 | child: Text("浮动", style: TextStyle(fontSize: 18)),
24 | )),
25 | ),
26 | ],
27 | ),
28 | );
29 | }
30 | }
31 |
32 | class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
33 | _SliverAppBarDelegate({
34 | required this.minHeight,
35 | required this.maxHeight,
36 | required this.child,
37 | });
38 |
39 | final double minHeight;
40 | final double maxHeight;
41 | final Widget child;
42 |
43 | @override
44 | double get minExtent => minHeight;
45 |
46 | @override
47 | double get maxExtent => max(maxHeight, minHeight);
48 |
49 | @override
50 | Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
51 | return new SizedBox.expand(child: child);
52 | }
53 |
54 | @override
55 | bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
56 | return maxHeight != oldDelegate.maxHeight || minHeight != oldDelegate.minHeight || child != oldDelegate.child;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/lib/components/column_menu_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/self_icon.dart';
3 | import 'package:flutter_maimai/constants.dart';
4 |
5 | const _defaultExt = Text('');
6 |
7 | class ColumnMenuItem extends StatelessWidget {
8 | const ColumnMenuItem({
9 | Key? key,
10 | this.ext = _defaultExt,
11 | required this.value,
12 | required this.press,
13 | }) : super(key: key);
14 |
15 | final Widget ext;
16 | final String value;
17 | final VoidCallback press;
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | return InkWell(
22 | onTap: press,
23 | child: Container(
24 | height: iColumnMenuHeight,
25 | decoration: BoxDecoration(
26 | color: Colors.transparent,
27 | border: new Border(
28 | bottom: BorderSide(color: Colors.black.withOpacity(0.03), width: 1),
29 | ),
30 | ),
31 | child: Row(
32 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
33 | children: [
34 | Text(
35 | value,
36 | style: TextStyle(
37 | fontSize: 16,
38 | fontWeight: FontWeight.w500,
39 | ),
40 | ),
41 | Expanded(
42 | flex: 1,
43 | child: Row(
44 | mainAxisAlignment: MainAxisAlignment.end,
45 | children: [
46 | ext,
47 | ],
48 | ),
49 | ),
50 | SizedBox(width: iDefaultPadding / 4),
51 | Icon(
52 | SelfIcon.ArrowRightFilling,
53 | size: 12,
54 | color: Colors.black26,
55 | ),
56 | ],
57 | ),
58 | ),
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | flutter_maimai
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 | CADisableMinimumFrameDurationOnPhone
45 |
46 | UIApplicationSupportsIndirectInputEvents
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/lib/pages/contact/components/friend_msg.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/badge.dart';
3 | import 'package:flutter_maimai/components/card_container.dart';
4 | import 'package:flutter_maimai/constants.dart';
5 | import 'package:flutter_maimai/mock/contact.dart';
6 | import 'package:flutter_maimai/pages/contact/components/friend_msg_item.dart';
7 |
8 | class FriendMsg extends StatefulWidget {
9 | const FriendMsg({Key? key}) : super(key: key);
10 |
11 | @override
12 | _FriendMsgState createState() => _FriendMsgState();
13 | }
14 |
15 | class _FriendMsgState extends State {
16 | List msgList = FriendMsgList;
17 |
18 | Widget _buildTitle() {
19 | return Padding(
20 | padding: EdgeInsets.symmetric(vertical: iDefaultPadding),
21 | child: Row(
22 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
23 | children: [
24 | Text('好友动态', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500, color: Colors.black)),
25 | SelfBadge('', dot: true, fixed: false),
26 | ],
27 | ),
28 | );
29 | }
30 |
31 | Widget _buildMsgList() {
32 | return SizedBox(
33 | height: 100,
34 | child: ListView.builder(
35 | itemBuilder: (context, index) {
36 | return FriendMsgItem(
37 | message: msgList[index],
38 | press: () {},
39 | );
40 | },
41 | itemCount: msgList.length,
42 | ),
43 | );
44 | }
45 |
46 | @override
47 | Widget build(BuildContext context) {
48 | return CardContainer(
49 | padding: EdgeInsets.only(left: iDefaultPadding, right: iDefaultPadding, bottom: iDefaultPadding),
50 | child: Column(
51 | children: [
52 | _buildTitle(),
53 | _buildMsgList(),
54 | ],
55 | ),
56 | );
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/lib/components/app_ad.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 |
4 | const EdgeInsets _margin = EdgeInsets.only(
5 | left: iDefaultPadding,
6 | right: iDefaultPadding,
7 | bottom: iDefaultPadding,
8 | );
9 |
10 | class BannerAd extends StatelessWidget {
11 | const BannerAd({Key? key}) : super(key: key);
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return Container();
16 | }
17 | }
18 |
19 | class ImageAd extends StatelessWidget {
20 | const ImageAd(
21 | this.image, {
22 | Key? key,
23 | this.margin = _margin,
24 | }) : super(key: key);
25 |
26 | final String image;
27 | final EdgeInsets margin;
28 |
29 | @override
30 | Widget build(BuildContext context) {
31 | Size size = MediaQuery.of(context).size;
32 | return Container(
33 | height: 120,
34 | width: size.width,
35 | margin: margin,
36 | decoration: BoxDecoration(
37 | borderRadius: iBorderRadius,
38 | image: new DecorationImage(
39 | image: new NetworkImage(image),
40 | fit: BoxFit.cover,
41 | ),
42 | ),
43 | child: Stack(
44 | children: [
45 | Positioned(
46 | top: 0,
47 | right: 0,
48 | child: Container(
49 | height: 18,
50 | width: 30,
51 | decoration: BoxDecoration(
52 | color: Colors.white.withOpacity(0.4),
53 | borderRadius: BorderRadius.only(bottomLeft: Radius.circular(10)),
54 | ),
55 | child: Center(
56 | child: Text(
57 | '广告',
58 | style: TextStyle(color: Colors.white, fontSize: 10),
59 | ),
60 | ),
61 | ),
62 | ),
63 | ],
64 | ),
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/lib/components/app_navbar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 | import 'package:flutter_svg/flutter_svg.dart';
4 |
5 | class AppNavBar extends StatelessWidget {
6 | const AppNavBar({Key? key}) : super(key: key);
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return Padding(
11 | padding: EdgeInsets.only(
12 | left: iDefaultPadding,
13 | // bottom: iDefaultPadding,
14 | right: iDefaultPadding,
15 | ),
16 | child: Container(
17 | decoration: BoxDecoration(
18 | color: iAppBottomBarColor,
19 | borderRadius: BorderRadius.circular(15),
20 | // boxShadow: [
21 | // BoxShadow(
22 | // offset: Offset(0, -10),
23 | // blurRadius: 35,
24 | // color: iPrimaryColor.withOpacity(0.38),
25 | // ),
26 | // ],
27 | ),
28 | child: Row(
29 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
30 | children: [
31 | IconButton(
32 | onPressed: () {},
33 | icon: SvgPicture.asset('assets/icons/flower.svg'),
34 | ),
35 | IconButton(
36 | onPressed: () {},
37 | icon: SvgPicture.asset('assets/icons/heart-icon.svg'),
38 | ),
39 | IconButton(
40 | onPressed: () {},
41 | icon: SvgPicture.asset('assets/icons/heart-icon.svg'),
42 | ),
43 | IconButton(
44 | onPressed: () {},
45 | icon: SvgPicture.asset('assets/icons/heart-icon.svg'),
46 | ),
47 | IconButton(
48 | onPressed: () {},
49 | icon: SvgPicture.asset('assets/icons/user-icon.svg'),
50 | ),
51 | ],
52 | ),
53 | ),
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 30
30 |
31 | sourceSets {
32 | main.java.srcDirs += 'src/main/kotlin'
33 | }
34 |
35 | defaultConfig {
36 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
37 | applicationId "com.example.flutter_maimai"
38 | minSdkVersion 16
39 | targetSdkVersion 30
40 | versionCode flutterVersionCode.toInteger()
41 | versionName flutterVersionName
42 | }
43 |
44 | buildTypes {
45 | release {
46 | // TODO: Add your own signing config for the release build.
47 | // Signing with the debug keys for now, so `flutter run --release` works.
48 | signingConfig signingConfigs.debug
49 | }
50 | }
51 | }
52 |
53 | flutter {
54 | source '../..'
55 | }
56 |
57 | dependencies {
58 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
59 | }
60 |
--------------------------------------------------------------------------------
/lib/pages/contact/components/request_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/card_container.dart';
3 | import 'package:flutter_maimai/components/self_icon.dart';
4 | import 'package:flutter_maimai/constants.dart';
5 |
6 | class RequestList extends StatelessWidget {
7 | const RequestList({Key? key}) : super(key: key);
8 |
9 | Widget _buildTitle() {
10 | return Padding(
11 | padding: EdgeInsets.symmetric(vertical: iDefaultPadding),
12 | child: Row(
13 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
14 | children: [
15 | Text(
16 | '待处理请求',
17 | style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500, color: Colors.black),
18 | ),
19 | Row(
20 | children: [
21 | Text(
22 | '管理我的请求',
23 | style: TextStyle(fontSize: 16, color: Colors.black38),
24 | ),
25 | Icon(
26 | SelfIcon.ArrowRightFilling,
27 | color: Colors.black26,
28 | size: 12,
29 | ),
30 | ],
31 | ),
32 | ],
33 | ),
34 | );
35 | }
36 |
37 | Widget _buildContent() {
38 | return Padding(
39 | padding: EdgeInsets.symmetric(vertical: iDefaultPadding / 2),
40 | child: Center(
41 | child: Text(
42 | '暂无好友请求,先来添加认识的好友吧!',
43 | style: TextStyle(
44 | fontSize: 14,
45 | color: Colors.black26,
46 | ),
47 | ),
48 | ),
49 | );
50 | }
51 |
52 | @override
53 | Widget build(BuildContext context) {
54 | return CardContainer(
55 | padding: EdgeInsets.only(left: iDefaultPadding, right: iDefaultPadding, bottom: iDefaultPadding),
56 | child: Column(
57 | children: [
58 | _buildTitle(),
59 | _buildContent(),
60 | ],
61 | ),
62 | );
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/lib/mock/message.dart:
--------------------------------------------------------------------------------
1 | List MessageMockList = [
2 | {
3 | "avatar": "https://wap.duoziwang.com/uploads/1512/1-1512261552020-L.jpg",
4 | "name": "红包小助手",
5 | "isOfficial": true,
6 | "company": "",
7 | "isCompany": false,
8 | "tip": "这个是消息的提示内容可",
9 | "date": "08-16",
10 | "vip": false,
11 | },
12 | {
13 | "avatar": "https://img2.baidu.com/it/u=1879129369,3648280576&fm=253&fmt=auto&app=138&f=JPEG?w=400&h=400",
14 | "name": "脉脉小助手",
15 | "isOfficial": true,
16 | "company": "",
17 | "isCompany": false,
18 | "tip": "这个是消息的提示内容可",
19 | "date": "08-16",
20 | "vip": false,
21 | },
22 | {
23 | "avatar":
24 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20180520%2F0473e00bdfd2476fbe0c228a45a1652c.jpeg&refer=http%3A%2F%2F5b0988e595225.cdn.sohucs.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1631705350&t=925858ee5e392535d8f58d30b2219701",
25 | "name": "机遇小助手",
26 | "isOfficial": true,
27 | "company": "",
28 | "isCompany": false,
29 | "tip": "这个是消息的提示内容可",
30 | "date": "08-16",
31 | "vip": false,
32 | },
33 | {
34 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/13625975163/1000.jpg",
35 | "name": "活动运营官",
36 | "isOfficial": true,
37 | "company": "",
38 | "isCompany": false,
39 | "tip": "这个是消息的提示内容可",
40 | "date": "08-16",
41 | "vip": false,
42 | },
43 | {
44 | "avatar":
45 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fq_70%2Cc_zoom%2Cw_640%2Fimages%2F20180831%2F5055651e9ece4799b3baa4dddd4d1e25.jpeg&refer=http%3A%2F%2F5b0988e595225.cdn.sohucs.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1631705350&t=8aef574e96578cdfafb80d70c800c5b9",
46 | "name": "好友XXX",
47 | "isOfficial": true,
48 | "company": "",
49 | "isCompany": false,
50 | "tip": "这个是消息的提示内容可",
51 | "date": "08-16",
52 | "vip": false,
53 | },
54 | ];
55 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_maimai
2 | description: A new Flutter project.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `pub publish`. This is preferred for private packages.
6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
7 |
8 | # The following defines the version and build number for your application.
9 | # A version number is three numbers separated by dots, like 1.2.43
10 | # followed by an optional build number separated by a +.
11 | # Both the version and the builder number may be overridden in flutter
12 | # build by specifying --build-name and --build-number, respectively.
13 | # In Android, build-name is used as versionName while build-number used as versionCode.
14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
16 | # Read more about iOS versioning at
17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
18 | version: 1.0.0+1
19 |
20 | environment:
21 | sdk: ">=2.12.0 <3.0.0"
22 |
23 | dependencies:
24 | flutter:
25 | sdk: flutter
26 | cupertino_icons: ^1.0.2
27 | # it helps us to use SVG
28 | flutter_svg: ^0.22.0
29 |
30 | dev_dependencies:
31 | flutter_test:
32 | sdk: flutter
33 |
34 | # For information on the generic Dart part of this file, see the
35 | # following page: https://dart.dev/tools/pub/pubspec
36 |
37 | # The following section is specific to Flutter.
38 | flutter:
39 |
40 | # The following line ensures that the Material Icons font is
41 | # included with your application, so that you can use the icons in
42 | # the material Icons class.
43 | uses-material-design: true
44 |
45 | # To add assets to your application, add an assets section, like this:
46 | assets:
47 | - assets/icons/
48 | - assets/images/
49 |
50 | fonts:
51 | - family: iconfont
52 | fonts:
53 | - asset: assets/fonts/iconfont.ttf
54 | #
55 | # For details regarding fonts from package dependencies,
56 | # see https://flutter.dev/custom-fonts/#from-packages
57 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
13 |
17 |
21 |
26 |
30 |
31 |
32 |
33 |
34 |
35 |
37 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/lib/mock/contact.dart:
--------------------------------------------------------------------------------
1 | List FriendMsgList = [
2 | {
3 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659087/1000",
4 | "nickname": "张三",
5 | "tip": "#项目经理",
6 | },
7 | {
8 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659094/1000",
9 | "nickname": "李四",
10 | "tip": "#Web前端",
11 | },
12 | {
13 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659379/1000",
14 | "nickname": "王五",
15 | "tip": "正在招聘",
16 | },
17 | {
18 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659613/1000",
19 | "nickname": "赵六",
20 | "tip": "#PM",
21 | },
22 | ];
23 |
24 | String QinghuaLogo =
25 | 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic4.zhimg.com%2F50%2Fv2-f2707167a67b3b71cc5b450e3246862d_hd.jpg&refer=http%3A%2F%2Fpic4.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1631769511&t=e16517404c5d40388daa0e2fa8919fc8';
26 |
27 | List ContactMockList = [
28 | {
29 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659087/1000",
30 | "name": "张三",
31 | "post": "杭州CCC公司 前端开发",
32 | "vip": false,
33 | "tag": ["正在招聘", "10家大公司人脉"],
34 | "other": ["云计算/计算机"],
35 | },
36 | {
37 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659094/1000",
38 | "name": "李四",
39 | "post": "杭州XXX网络有限责任公司前端开发工程师",
40 | "vip": false,
41 | "tag": ["正在招聘", "企业级软件"],
42 | "other": ["有超过2000位好友"],
43 | },
44 | {
45 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659379/1000",
46 | "name": "王五",
47 | "post": "杭州CCC公司 前端开发",
48 | "vip": false,
49 | "tag": ["正在招聘", "10家大公司人脉"],
50 | "other": ["云计算/计算机"],
51 | },
52 | {
53 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659087/1000",
54 | "name": "张三",
55 | "post": "杭州CCC公司 前端开发",
56 | "vip": false,
57 | "tag": ["正在招聘", "10家大公司人脉"],
58 | "other": ["云计算/计算机"],
59 | },
60 | {
61 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659094/1000",
62 | "name": "李四",
63 | "post": "杭州XXX网络有限责任公司前端开发工程师",
64 | "vip": false,
65 | "tag": ["正在招聘", "企业级软件"],
66 | "other": ["有超过2000位好友"],
67 | },
68 | {
69 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659379/1000",
70 | "name": "王五",
71 | "post": "杭州CCC公司 前端开发",
72 | "vip": false,
73 | "tag": ["正在招聘", "10家大公司人脉"],
74 | "other": ["云计算/计算机"],
75 | },
76 | ];
77 |
--------------------------------------------------------------------------------
/lib/pages/home/components/follows_recommend.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/card_container.dart';
3 | import 'package:flutter_maimai/components/self_icon.dart';
4 | import 'package:flutter_maimai/constants.dart';
5 | import 'package:flutter_maimai/mock/home.dart';
6 | import 'package:flutter_maimai/pages/home/components/user_item.dart';
7 |
8 | class FollowsRecommend extends StatefulWidget {
9 | const FollowsRecommend({Key? key}) : super(key: key);
10 |
11 | @override
12 | _FollowsRecommendState createState() => _FollowsRecommendState();
13 | }
14 |
15 | class _FollowsRecommendState extends State {
16 | List userList = FollowsRecommendMockList;
17 |
18 | Widget _buildTitle() {
19 | return Container(
20 | padding: EdgeInsets.symmetric(vertical: iDefaultPadding / 2),
21 | child: Text(
22 | '为你推荐',
23 | style: TextStyle(fontSize: 20, color: Colors.black, fontWeight: FontWeight.w500),
24 | ),
25 | );
26 | }
27 |
28 | Widget _buildContent() {
29 | List _list = [];
30 | for (int i = 0; i < userList.length; i++) {
31 | _list.add(UserItem(userList[i]));
32 | }
33 | return Column(
34 | children: _list,
35 | );
36 | }
37 |
38 | Widget _buildFooter() {
39 | return Container(
40 | padding: EdgeInsets.symmetric(vertical: iDefaultPadding / 1.5),
41 | decoration: BoxDecoration(
42 | border: new Border(top: BorderSide(width: 0.3, color: Colors.black12)),
43 | ),
44 | child: Row(
45 | mainAxisAlignment: MainAxisAlignment.center,
46 | crossAxisAlignment: CrossAxisAlignment.center,
47 | children: [
48 | Text('查看更多', style: TextStyle(fontSize: 16, color: Colors.black54)),
49 | SizedBox(width: iDefaultPadding / 4),
50 | Icon(SelfIcon.ArrowRightFilling, size: 14, color: Colors.black45),
51 | ],
52 | ),
53 | );
54 | }
55 |
56 | @override
57 | Widget build(BuildContext context) {
58 | return CardContainer(
59 | padding: EdgeInsets.symmetric(horizontal: iDefaultPadding),
60 | child: Column(
61 | crossAxisAlignment: CrossAxisAlignment.start,
62 | children: [
63 | _buildTitle(),
64 | _buildContent(),
65 | _buildFooter(),
66 | ],
67 | ),
68 | );
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/lib/pages/chance/components/follow_market.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/card_container.dart';
3 | import 'package:flutter_maimai/components/menu_item.dart';
4 | import 'package:flutter_maimai/components/self_icon.dart';
5 | import 'package:flutter_maimai/constants.dart';
6 |
7 | class FollowMarket extends StatelessWidget {
8 | const FollowMarket({Key? key}) : super(key: key);
9 |
10 | Widget _buildTitle() {
11 | return Padding(
12 | padding: EdgeInsets.all(iDefaultPadding),
13 | child: Row(
14 | crossAxisAlignment: CrossAxisAlignment.center,
15 | children: [
16 | CircleAvatar(
17 | radius: 15,
18 | backgroundImage: AssetImage('assets/images/avatar.png'),
19 | ),
20 | Container(
21 | margin: EdgeInsets.only(left: iDefaultPadding / 2),
22 | child: Row(
23 | crossAxisAlignment: CrossAxisAlignment.center,
24 | children: [
25 | Text(
26 | '关注行情',
27 | style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
28 | ),
29 | Icon(
30 | SelfIcon.ArrowRightFilling,
31 | size: 12,
32 | ),
33 | ],
34 | ),
35 | ),
36 | ],
37 | ),
38 | );
39 | }
40 |
41 | Widget _buildMenuList() {
42 | return Row(
43 | mainAxisAlignment: MainAxisAlignment.spaceAround,
44 | children: [
45 | MenuItem(
46 | name: '我的简历',
47 | press: () {},
48 | icon: SelfIcon.FolderFilling,
49 | ),
50 | MenuItem(
51 | name: '我的申请',
52 | press: () {},
53 | icon: SelfIcon.HistoryFilling,
54 | ),
55 | MenuItem(
56 | name: '职位收藏',
57 | press: () {},
58 | icon: SelfIcon.FavoriteFilling,
59 | ),
60 | MenuItem(
61 | name: '更多',
62 | press: () {},
63 | icon: SelfIcon.DynamicFilling,
64 | ),
65 | ],
66 | );
67 | }
68 |
69 | @override
70 | Widget build(BuildContext context) {
71 | return CardContainer(
72 | padding: EdgeInsets.only(bottom: iDefaultPadding),
73 | child: Column(
74 | children: [
75 | _buildTitle(),
76 | // Spacer(),
77 | _buildMenuList(),
78 | ],
79 | ),
80 | );
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/lib/components/search_field.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/self_icon.dart';
3 | import 'package:flutter_maimai/constants.dart';
4 |
5 | class SearchField extends StatelessWidget {
6 | const SearchField({
7 | Key? key,
8 | this.placeholder = '输入搜索内容',
9 | this.btnText = '搜索',
10 | this.haveButton = false,
11 | this.haveGap = true,
12 | }) : super(key: key);
13 |
14 | final String placeholder, btnText;
15 | final bool haveButton, haveGap;
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return Container(
20 | margin: EdgeInsets.all(haveGap ? iDefaultPadding : 0),
21 | padding: EdgeInsets.only(right: haveGap ? 5 : 0, left: haveGap ? 10 : 0),
22 | height: 45,
23 | decoration: BoxDecoration(
24 | color: Colors.white,
25 | borderRadius: BorderRadius.all(Radius.circular(45)),
26 | ),
27 | child: Row(
28 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
29 | children: [
30 | Icon(
31 | SelfIcon.Search,
32 | size: 20,
33 | ),
34 | Expanded(
35 | flex: 1,
36 | child: Padding(
37 | padding: EdgeInsets.symmetric(horizontal: 10),
38 | child: TextField(
39 | onChanged: (value) {},
40 | decoration: InputDecoration(
41 | hintText: placeholder,
42 | hintStyle: TextStyle(color: Colors.black45, fontSize: 16),
43 | enabledBorder: InputBorder.none,
44 | focusedBorder: InputBorder.none,
45 | ),
46 | ),
47 | ),
48 | ),
49 | haveButton
50 | ? ElevatedButton(
51 | onPressed: () {},
52 | child: Text(btnText),
53 | style: ButtonStyle(
54 | padding: MaterialStateProperty.all(EdgeInsets.all(1)),
55 | textStyle: MaterialStateProperty.all(TextStyle(
56 | fontSize: 14,
57 | )),
58 | backgroundColor: MaterialStateProperty.all(iPrimaryColor),
59 | shape: MaterialStateProperty.all(
60 | RoundedRectangleBorder(
61 | borderRadius: BorderRadius.all(Radius.circular(20)),
62 | ),
63 | ),
64 | ),
65 | )
66 | : Text(''),
67 | ],
68 | ),
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/lib/pages/message/components/message_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 | import 'package:flutter_maimai/pages/message/components/account_type_tag.dart';
4 |
5 | const double itemHeight = 60.0;
6 |
7 | class MessageItem extends StatelessWidget {
8 | const MessageItem({
9 | Key? key,
10 | required this.message,
11 | required this.press,
12 | }) : super(key: key);
13 |
14 | final message;
15 | final VoidCallback press;
16 |
17 | Widget _buildTitle(Size size) {
18 | TextStyle _titleStyle = TextStyle(color: Colors.black, fontSize: 20, fontWeight: FontWeight.bold);
19 | TextStyle _dateStyle = TextStyle(color: Colors.black26, fontSize: 14, fontWeight: FontWeight.w400);
20 | return Container(
21 | width: size.width * 0.7,
22 | child: Row(
23 | // mainAxisAlignment: MainAxisAlignment.start,
24 | children: [
25 | Text(message['name'], style: _titleStyle),
26 | AccountTypeTag(isCompany: message['isCompany'], isOfficial: message['isOfficial']),
27 | Spacer(),
28 | Text(message['date'], style: _dateStyle),
29 | ],
30 | ),
31 | );
32 | }
33 |
34 | Widget _buildContent(Size size) {
35 | TextStyle _tipStyle = TextStyle(color: Colors.black45, fontSize: 16, fontWeight: FontWeight.w500);
36 | return Container(
37 | width: size.width * 0.7,
38 | child: Text(
39 | message['tip'],
40 | style: _tipStyle,
41 | overflow: TextOverflow.ellipsis,
42 | maxLines: 1,
43 | ),
44 | );
45 | }
46 |
47 | @override
48 | Widget build(BuildContext context) {
49 | Size size = MediaQuery.of(context).size;
50 | return GestureDetector(
51 | onTap: press,
52 | child: Container(
53 | color: Colors.white,
54 | width: size.width,
55 | height: itemHeight + iDefaultPadding * 2,
56 | padding: EdgeInsets.symmetric(horizontal: iDefaultPadding, vertical: iDefaultPadding),
57 | child: Row(
58 | children: [
59 | CircleAvatar(
60 | radius: itemHeight / 2,
61 | backgroundImage: NetworkImage(message['avatar']),
62 | ),
63 | SizedBox(width: 10),
64 | Column(
65 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
66 | crossAxisAlignment: CrossAxisAlignment.start,
67 | children: [
68 | _buildTitle(size),
69 | _buildContent(size),
70 | ],
71 | ),
72 | ],
73 | ),
74 | ),
75 | );
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/lib/pages/home/components/ask_menu_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/badge.dart';
3 | import 'package:flutter_maimai/components/self_icon.dart';
4 | import 'package:flutter_maimai/constants.dart';
5 |
6 | class AskMenuItem extends StatelessWidget {
7 | const AskMenuItem({
8 | Key? key,
9 | required this.title,
10 | required this.desc,
11 | required this.icon,
12 | required this.press,
13 | this.isLink = false,
14 | }) : super(key: key);
15 |
16 | final String title, desc;
17 | final IconData icon;
18 | final VoidCallback press;
19 | final bool isLink;
20 | @override
21 | Widget build(BuildContext context) {
22 | return Container(
23 | height: 80,
24 | padding: EdgeInsets.symmetric(horizontal: iDefaultPadding / 2, vertical: iDefaultPadding / 2),
25 | decoration: BoxDecoration(
26 | borderRadius: iBorderRadius,
27 | color: Colors.white,
28 | ),
29 | child: Row(
30 | children: [
31 | Container(
32 | width: 60,
33 | height: 60,
34 | margin: EdgeInsets.only(right: iDefaultPadding / 2),
35 | child: Icon(
36 | icon,
37 | size: 40,
38 | ),
39 | ),
40 | Expanded(
41 | child: Container(
42 | height: 45,
43 | child: Column(
44 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
45 | crossAxisAlignment: CrossAxisAlignment.start,
46 | children: [
47 | Text(
48 | title,
49 | style: TextStyle(
50 | fontSize: 16,
51 | fontWeight: FontWeight.w500,
52 | color: Colors.black,
53 | ),
54 | ),
55 | Text(
56 | desc,
57 | style: TextStyle(
58 | fontSize: 14,
59 | fontWeight: FontWeight.w400,
60 | color: Colors.black38,
61 | ),
62 | ),
63 | ],
64 | ),
65 | ),
66 | ),
67 | isLink
68 | ? Row(
69 | children: [
70 | SelfBadge('', dot: true, fixed: false),
71 | SizedBox(width: iDefaultPadding / 4),
72 | Icon(SelfIcon.ArrowRight, size: 16, color: Colors.black38),
73 | ],
74 | )
75 | : Text(''),
76 | ],
77 | ),
78 | );
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/lib/pages/owner/components/owner_info.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/card_container.dart';
3 | import 'package:flutter_maimai/components/self_icon.dart';
4 | import 'package:flutter_maimai/constants.dart';
5 | import 'package:flutter_maimai/pages/owner/components/owner_detail_item.dart';
6 | import 'package:flutter_maimai/pages/owner/components/owner_detail_line.dart';
7 |
8 | class OwnerInfo extends StatelessWidget {
9 | const OwnerInfo({Key? key}) : super(key: key);
10 |
11 | Widget _buildInfo() {
12 | TextStyle _style = TextStyle(color: Colors.black87, fontSize: 16, fontWeight: FontWeight.w500);
13 | return Row(
14 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
15 | crossAxisAlignment: CrossAxisAlignment.start,
16 | children: [
17 | CircleAvatar(
18 | radius: 40,
19 | backgroundImage: AssetImage('assets/images/avatar.png'),
20 | ),
21 | Expanded(
22 | flex: 1,
23 | child: Padding(
24 | padding: EdgeInsets.symmetric(horizontal: iDefaultPadding / 2),
25 | child: Column(
26 | crossAxisAlignment: CrossAxisAlignment.start,
27 | children: [
28 | Text(
29 | '小码农',
30 | style: TextStyle(fontSize: 25, color: Colors.black, fontWeight: FontWeight.bold),
31 | ),
32 | Text('悠点互娱', style: _style),
33 | Text('CEO', style: _style),
34 | ],
35 | ),
36 | ),
37 | ),
38 | Row(
39 | children: [
40 | Text('个人主页'),
41 | Icon(SelfIcon.ArrowRightFilling, size: 12),
42 | ],
43 | ),
44 | ],
45 | );
46 | }
47 |
48 | Widget _buildDetail() {
49 | return Padding(
50 | padding: EdgeInsets.only(top: iDefaultPadding),
51 | child: Row(
52 | children: [
53 | OwnerDetailItem(label: '好友', value: 1),
54 | OwnerDetailLine(),
55 | OwnerDetailItem(label: '影响力', value: 22),
56 | OwnerDetailLine(),
57 | OwnerDetailItem(label: '访客', value: 10),
58 | OwnerDetailLine(),
59 | OwnerDetailItem(label: '内容发布', value: 0),
60 | ],
61 | ),
62 | );
63 | }
64 |
65 | @override
66 | Widget build(BuildContext context) {
67 | return CardContainer(
68 | margin: EdgeInsets.only(left: iDefaultPadding, right: iDefaultPadding, bottom: iDefaultPadding, top: iDefaultPadding / 2),
69 | padding: EdgeInsets.all(iDefaultPadding),
70 | child: Column(
71 | children: [
72 | _buildInfo(),
73 | _buildDetail(),
74 | ],
75 | ),
76 | );
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/lib/pages/home/components/user_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/vip_badge.dart';
3 | import 'package:flutter_maimai/constants.dart';
4 |
5 | class UserItem extends StatelessWidget {
6 | const UserItem(this.userInfo, {Key? key}) : super(key: key);
7 |
8 | final userInfo;
9 |
10 | Widget _buildPostInfo(Size size) {
11 | String _company = userInfo['company'];
12 | String _post = userInfo['post'];
13 | bool _isVip = userInfo['vip'];
14 | return Row(
15 | mainAxisAlignment: MainAxisAlignment.start,
16 | children: [
17 | SizedBox(
18 | width: size.width * 0.4,
19 | child: Text(
20 | '$_company$_post',
21 | overflow: TextOverflow.ellipsis,
22 | style: TextStyle(color: Colors.black45, fontSize: 14),
23 | ),
24 | ),
25 | _isVip ? VipBadge() : Text(''),
26 | ],
27 | );
28 | }
29 |
30 | Widget _buildMoreInfo() {
31 | int _count = userInfo['msgCount'];
32 | return _count > 0 ? Text('近30天发布了$_count个动态', style: TextStyle(color: Colors.black45, fontSize: 14)) : Text('');
33 | }
34 |
35 | @override
36 | Widget build(BuildContext context) {
37 | Size size = MediaQuery.of(context).size;
38 | return Container(
39 | margin: EdgeInsets.symmetric(vertical: iDefaultPadding / 2),
40 | child: Row(
41 | children: [
42 | CircleAvatar(
43 | radius: 25,
44 | backgroundImage: NetworkImage(userInfo['avatar']),
45 | ),
46 | SizedBox(width: iDefaultPadding / 2),
47 | Expanded(
48 | child: Column(
49 | mainAxisAlignment: MainAxisAlignment.center,
50 | crossAxisAlignment: CrossAxisAlignment.start,
51 | children: [
52 | Text(
53 | userInfo['name'],
54 | style: TextStyle(fontSize: 18, color: Colors.black, fontWeight: FontWeight.w500),
55 | ),
56 | _buildPostInfo(size),
57 | _buildMoreInfo(),
58 | ],
59 | ),
60 | ),
61 | TextButton(
62 | onPressed: () {},
63 | child: Text(
64 | '关注',
65 | style: TextStyle(color: Colors.white),
66 | ),
67 | style: ButtonStyle(
68 | backgroundColor: MaterialStateProperty.all(iPrimaryColor),
69 | padding: MaterialStateProperty.all(EdgeInsets.symmetric(horizontal: iDefaultPadding)),
70 | shape: MaterialStateProperty.all(
71 | RoundedRectangleBorder(
72 | borderRadius: BorderRadius.all(Radius.circular(20)),
73 | ),
74 | ),
75 | ),
76 | ),
77 | ],
78 | ),
79 | );
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/lib/pages/home/components/choiceness_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/constants.dart';
3 |
4 | class ChoicenessItem extends StatelessWidget {
5 | const ChoicenessItem({
6 | Key? key,
7 | required this.item,
8 | }) : super(key: key);
9 |
10 | final item;
11 |
12 | Widget _buildTag() {
13 | String _tag = item['tag'];
14 | if (_tag == '') return Text('');
15 | return Positioned(
16 | right: iDefaultPadding / 2,
17 | top: iDefaultPadding / 2,
18 | child: Container(
19 | width: 25,
20 | height: 14,
21 | // padding: EdgeInsets.symmetric(horizontal: 5, ),
22 | decoration: BoxDecoration(
23 | color: Colors.red,
24 | borderRadius: BorderRadius.all(Radius.circular(4)),
25 | ),
26 | child: Center(
27 | child: Text(_tag, style: TextStyle(color: Colors.white, fontSize: 10, fontStyle: FontStyle.italic)),
28 | ),
29 | ),
30 | );
31 | }
32 |
33 | Widget _buildTopImage() {
34 | return Container(
35 | width: 170,
36 | height: 60,
37 | decoration: BoxDecoration(
38 | borderRadius: BorderRadius.only(
39 | topLeft: Radius.circular(5),
40 | topRight: Radius.circular(5),
41 | ),
42 | image: DecorationImage(
43 | image: NetworkImage(item['picture']),
44 | fit: BoxFit.cover,
45 | ),
46 | ),
47 | child: Stack(
48 | children: [
49 | Positioned(
50 | bottom: iDefaultPadding / 4,
51 | left: iDefaultPadding / 4,
52 | child: Text(
53 | item['name'],
54 | style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: Colors.white),
55 | ),
56 | ),
57 | _buildTag(),
58 | ],
59 | ),
60 | );
61 | }
62 |
63 | @override
64 | Widget build(BuildContext context) {
65 | return Container(
66 | width: 170,
67 | // height: 120,
68 | margin: EdgeInsets.only(right: iDefaultPadding / 2),
69 | decoration: BoxDecoration(
70 | // color: Colors.yellow,
71 | border: Border.all(width: 0.3, color: Colors.black38),
72 | borderRadius: BorderRadius.all(Radius.circular(5)),
73 | ),
74 | child: Column(
75 | crossAxisAlignment: CrossAxisAlignment.start,
76 | children: [
77 | _buildTopImage(),
78 | Text(
79 | item['desc'],
80 | style: TextStyle(fontSize: 12, color: Colors.black38),
81 | ),
82 | Padding(
83 | padding: EdgeInsets.only(left: iDefaultPadding / 4),
84 | child: Text(
85 | item['content'],
86 | overflow: TextOverflow.ellipsis,
87 | maxLines: 2,
88 | style: TextStyle(),
89 | ),
90 | ),
91 | ],
92 | ),
93 | );
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/lib/common/customer_tab_size_indicator.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/widgets.dart';
3 |
4 | class CustomerTabSizeIndicator extends Decoration {
5 | final double width;
6 | final BorderSide borderSide;
7 | final EdgeInsetsGeometry insets;
8 |
9 | const CustomerTabSizeIndicator({
10 | this.borderSide = const BorderSide(width: 2.0, color: Colors.blue),
11 | this.insets = EdgeInsets.zero,
12 | this.width = 20,
13 | }) : assert(borderSide != null),
14 | assert(insets != null),
15 | assert(width != null);
16 | @override
17 | Decoration lerpFrom(Decoration? a, double t) {
18 | if (a is UnderlineTabIndicator) {
19 | return UnderlineTabIndicator(
20 | borderSide: BorderSide.lerp(a.borderSide, borderSide, t),
21 | insets: EdgeInsetsGeometry.lerp(a.insets, insets, t) as EdgeInsetsGeometry,
22 | );
23 | }
24 | return super.lerpFrom(a, t) as Decoration;
25 | }
26 |
27 | @override
28 | Decoration lerpTo(Decoration? b, double t) {
29 | if (b is CustomerTabSizeIndicator) {
30 | return CustomerTabSizeIndicator(
31 | borderSide: BorderSide.lerp(borderSide, b.borderSide, t),
32 | insets: EdgeInsetsGeometry.lerp(insets, b.insets, t) as EdgeInsetsGeometry,
33 | );
34 | }
35 | return super.lerpTo(b, t) as Decoration;
36 | }
37 |
38 | @override
39 | _MyUnderlinePainter createBoxPainter([VoidCallback? onChanged]) {
40 | return _MyUnderlinePainter(this, width, onChanged!);
41 | }
42 | }
43 |
44 | class _MyUnderlinePainter extends BoxPainter {
45 | final double width;
46 | _MyUnderlinePainter(this.decoration, this.width, VoidCallback onChanged)
47 | : assert(decoration != null),
48 | super(onChanged);
49 |
50 | final CustomerTabSizeIndicator decoration;
51 |
52 | BorderSide get borderSide => decoration.borderSide;
53 | EdgeInsetsGeometry get insets => decoration.insets;
54 |
55 | Rect _indicatorRectFor(Rect rect, TextDirection textDirection) {
56 | assert(rect != null);
57 | assert(textDirection != null);
58 | final Rect indicator = insets.resolve(textDirection).deflateRect(rect);
59 |
60 | //希望的宽度
61 | // double width = 20;
62 | //取中间坐标
63 | double cw = (indicator.left + indicator.right) / 2;
64 | return Rect.fromLTWH(cw - width / 2, indicator.bottom - borderSide.width, width, borderSide.width);
65 | }
66 |
67 | @override
68 | void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
69 | assert(configuration != null);
70 | assert(configuration.size != null);
71 | final Rect rect = offset & (configuration.size as Size);
72 | final TextDirection textDirection = configuration.textDirection as TextDirection;
73 | final Rect indicator = _indicatorRectFor(rect, textDirection).deflate(borderSide.width / 2.0);
74 | // 规定绘制为圆角
75 | final Paint paint = borderSide.toPaint()..strokeCap = StrokeCap.round;
76 | canvas.drawLine(indicator.bottomLeft, indicator.bottomRight, paint);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/lib/pages/home/components/ask_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/card_container.dart';
3 | import 'package:flutter_maimai/components/tag.dart';
4 | import 'package:flutter_maimai/constants.dart';
5 |
6 | class AskItem extends StatelessWidget {
7 | const AskItem(this.askInfo, {Key? key}) : super(key: key);
8 |
9 | final askInfo;
10 |
11 | Widget _buildMention() {
12 | List _mentionData = askInfo['mention'];
13 | List _mentionList = [];
14 | for (int i = 0; i < _mentionData.length; i++) {
15 | _mentionList.add(Padding(
16 | padding: EdgeInsets.only(top: iDefaultPadding / 4, right: iDefaultPadding / 2),
17 | child: Text(
18 | '@${_mentionData[i]}',
19 | style: TextStyle(color: iPrimaryColor, fontSize: 14),
20 | )));
21 | }
22 | return Row(
23 | children: _mentionList,
24 | );
25 | }
26 |
27 | Widget _buildAnswer() {
28 | var _answer = askInfo['answer'];
29 | return Column(
30 | children: [
31 | Padding(
32 | padding: EdgeInsets.only(top: iDefaultPadding, bottom: iDefaultPadding / 2),
33 | child: Row(
34 | children: [
35 | CircleAvatar(
36 | radius: 10,
37 | backgroundImage: NetworkImage(_answer['logo']),
38 | ),
39 | Padding(
40 | padding: EdgeInsets.only(left: iDefaultPadding / 4),
41 | child: Text(
42 | _answer["name"],
43 | style: TextStyle(fontSize: 14),
44 | ),
45 | ),
46 | Padding(
47 | padding: EdgeInsets.symmetric(horizontal: 4),
48 | child: Tag('ID', plain: true),
49 | ),
50 | Text(
51 | '回答:',
52 | style: TextStyle(color: Colors.black54),
53 | ),
54 | ],
55 | ),
56 | ),
57 | Text(
58 | _answer['content'],
59 | softWrap: true,
60 | overflow: TextOverflow.ellipsis,
61 | maxLines: 2,
62 | style: TextStyle(fontSize: 18, color: Colors.black54),
63 | )
64 | ],
65 | );
66 | }
67 |
68 | Widget _buildFooter() {
69 | int _count = askInfo['count'];
70 | int _likes = askInfo['likes'];
71 | return Padding(
72 | padding: EdgeInsets.only(top: iDefaultPadding / 2),
73 | child: Text('$_count 回答 · $_likes 人觉得靠谱', style: TextStyle(color: Colors.black38)),
74 | );
75 | }
76 |
77 | @override
78 | Widget build(BuildContext context) {
79 | return CardContainer(
80 | margin: EdgeInsets.only(left: iDefaultPadding / 2, right: iDefaultPadding / 2, bottom: iDefaultPadding / 2),
81 | padding: EdgeInsets.all(iDefaultPadding),
82 | child: Column(
83 | crossAxisAlignment: CrossAxisAlignment.start,
84 | children: [
85 | Text(
86 | askInfo['title'],
87 | style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500, color: Colors.black),
88 | ),
89 | _buildMention(),
90 | _buildAnswer(),
91 | _buildFooter(),
92 | ],
93 | ),
94 | );
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/lib/components/navbar_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/badge.dart';
3 | import 'package:flutter_maimai/constants.dart';
4 | import 'package:flutter_maimai/enums.dart';
5 |
6 | class NavbarItem extends StatelessWidget {
7 | const NavbarItem({
8 | Key? key,
9 | required this.isActive,
10 | required this.name,
11 | required this.icon,
12 | this.num = 0,
13 | this.type = NavbarMessageType.number,
14 | required this.press,
15 | }) : super(key: key);
16 |
17 | final bool isActive;
18 | final String name;
19 | final IconData icon;
20 | final int num;
21 | final NavbarMessageType type;
22 | final VoidCallback press;
23 |
24 | @override
25 | Widget build(BuildContext context) {
26 | Size size = MediaQuery.of(context).size;
27 | var itemWidth = (size.width * 0.9) / 5;
28 | bool isDot = type == NavbarMessageType.dot;
29 | // return
30 | return InkWell(
31 | onTap: press,
32 | child: Container(
33 | width: itemWidth,
34 | child: Stack(
35 | children: [
36 | SizedBox(
37 | width: itemWidth,
38 | child: Column(
39 | mainAxisAlignment: MainAxisAlignment.center,
40 | crossAxisAlignment: CrossAxisAlignment.center,
41 | children: [
42 | SizedBox(
43 | width: 40,
44 | height: 40,
45 | child: Icon(
46 | icon,
47 | color: isActive ? iNavbarActiveColor : iNavbarColor,
48 | ),
49 | ),
50 | Text(
51 | name,
52 | style: TextStyle(
53 | fontSize: 12,
54 | color: isActive ? iNavbarActiveColor : iNavbarColor,
55 | ),
56 | )
57 | ],
58 | ),
59 | ),
60 | num > 0
61 | ? SelfBadge(
62 | '$num',
63 | dot: isDot,
64 | right: isDot ? itemWidth * 0.3 : itemWidth * 0.16,
65 | top: isDot ? 10 : 4,
66 | )
67 | : Text(''),
68 | ],
69 | ),
70 | ),
71 | );
72 | }
73 | }
74 |
75 | class NavbarItemBadge extends StatelessWidget {
76 | const NavbarItemBadge({
77 | Key? key,
78 | required this.type,
79 | this.num = 0,
80 | }) : super(key: key);
81 |
82 | final int num;
83 | final NavbarMessageType type;
84 |
85 | Widget createBadge() {
86 | bool isDot = type == NavbarMessageType.dot;
87 | return Container(
88 | width: isDot ? 8 : 18,
89 | height: isDot ? 8 : 18,
90 | child: isDot
91 | ? Text('')
92 | : Center(
93 | child: Text('$num', style: TextStyle(fontSize: 10.0, color: Colors.white)),
94 | ),
95 | decoration: BoxDecoration(
96 | color: Colors.red,
97 | borderRadius: new BorderRadius.all(
98 | const Radius.circular(16.0),
99 | ),
100 | ),
101 | );
102 | }
103 |
104 | @override
105 | Widget build(BuildContext context) {
106 | return num > 0 ? createBadge() : Text('');
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/lib/pages/contact/components/contact_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/self_icon.dart';
3 | import 'package:flutter_maimai/constants.dart';
4 |
5 | class ContactItem extends StatelessWidget {
6 | const ContactItem({
7 | Key? key,
8 | required this.contact,
9 | required this.press,
10 | }) : super(key: key);
11 |
12 | final contact;
13 | final VoidCallback press;
14 |
15 | Widget _buildActionBtn() {
16 | return TextButton(
17 | onPressed: () {},
18 | style: ButtonStyle(
19 | backgroundColor: MaterialStateProperty.all(iPrimaryColor),
20 | padding: MaterialStateProperty.all(EdgeInsets.symmetric(horizontal: iDefaultPadding)),
21 | shape: MaterialStateProperty.all(
22 | RoundedRectangleBorder(
23 | borderRadius: BorderRadius.all(Radius.circular(20)),
24 | ),
25 | ),
26 | ),
27 | child: Row(
28 | children: [
29 | Icon(
30 | SelfIcon.Add,
31 | size: 16,
32 | color: Colors.white,
33 | ),
34 | SizedBox(width: iDefaultPadding / 4),
35 | Text(
36 | '好友',
37 | style: TextStyle(color: Colors.white),
38 | ),
39 | ],
40 | ),
41 | );
42 | }
43 |
44 | Widget _buildInfo() {
45 | List _tagsWidget = [];
46 | List _otherWidget = [];
47 | List _tags = contact['tag'];
48 | List _other = contact['other'];
49 | const _style = TextStyle(fontSize: 14, color: Colors.black38, fontWeight: FontWeight.w500);
50 | for (int i = 0; i < _tags.length; i++) {
51 | String item = _tags[i];
52 | _tagsWidget.add(Text(
53 | item,
54 | style: _style,
55 | ));
56 | _tagsWidget.add(SizedBox(width: 5));
57 | }
58 | for (int i = 0; i < _other.length; i++) {
59 | String item = _other[i];
60 | _otherWidget.add(Text(
61 | item,
62 | style: _style,
63 | ));
64 | }
65 | return Expanded(
66 | child: Padding(
67 | padding: EdgeInsets.only(left: iDefaultPadding / 2),
68 | child: Column(
69 | crossAxisAlignment: CrossAxisAlignment.start,
70 | children: [
71 | Text(
72 | contact['name'],
73 | style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500, color: Colors.black),
74 | ),
75 | Text(
76 | contact['post'],
77 | softWrap: true,
78 | style: TextStyle(fontSize: 14, color: Color(0xFFA9B2D6), fontWeight: FontWeight.w500),
79 | ),
80 | Row(children: _tagsWidget),
81 | Column(children: _otherWidget),
82 | ],
83 | ),
84 | ),
85 | );
86 | }
87 |
88 | @override
89 | Widget build(BuildContext context) {
90 | return Container(
91 | padding: EdgeInsets.all(iDefaultPadding),
92 | color: Colors.white,
93 | child: Row(
94 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
95 | crossAxisAlignment: CrossAxisAlignment.start,
96 | children: [
97 | CircleAvatar(
98 | radius: 30,
99 | backgroundImage: NetworkImage(contact['avatar']),
100 | ),
101 | _buildInfo(),
102 | _buildActionBtn(),
103 | ],
104 | ),
105 | );
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/navbar_item.dart';
3 | import 'package:flutter_maimai/components/self_icon.dart';
4 | import 'package:flutter_maimai/constants.dart';
5 | import 'package:flutter_maimai/enums.dart';
6 | import 'package:flutter_maimai/pages/chance/chance.dart';
7 | import 'package:flutter_maimai/pages/contact/contact.dart';
8 | import 'package:flutter_maimai/pages/home/home.dart';
9 | import 'package:flutter_maimai/pages/message/message.dart';
10 | import 'package:flutter_maimai/pages/owner/owner.dart';
11 | import 'package:flutter_maimai/router.dart';
12 |
13 | void main() {
14 | runApp(MyApp());
15 | }
16 |
17 | class MyApp extends StatelessWidget {
18 | @override
19 | Widget build(BuildContext context) {
20 | return MaterialApp(
21 | title: 'Flutter Maimai',
22 | debugShowCheckedModeBanner: false,
23 | home: MyHomePage(),
24 | routes: iPageRouter,
25 | // onGenerateRoute: pageRouterController,
26 | );
27 | }
28 | }
29 |
30 | class MyHomePage extends StatefulWidget {
31 | MyHomePage({Key? key}) : super(key: key);
32 |
33 | @override
34 | _MyHomePageState createState() => _MyHomePageState();
35 | }
36 |
37 | class _MyHomePageState extends State {
38 | int navIdx = 0;
39 | List pageList = [
40 | Home(),
41 | Contact(),
42 | Chance(),
43 | Message(),
44 | Owner(),
45 | ];
46 |
47 | void changeNavbar(int idx) {
48 | setState(() {
49 | navIdx = idx;
50 | });
51 | }
52 |
53 | @override
54 | Widget build(BuildContext context) {
55 | Size size = MediaQuery.of(context).size;
56 | return Scaffold(
57 | body: pageList[navIdx],
58 | backgroundColor: iAppBackgroundColor,
59 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
60 | floatingActionButton: Container(
61 | width: size.width * 0.9,
62 | height: iNavBarHeight,
63 | decoration: BoxDecoration(
64 | color: iAppBottomBarColor,
65 | borderRadius: BorderRadius.circular(20),
66 | ),
67 | child: Row(
68 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
69 | children: [
70 | NavbarItem(
71 | isActive: navIdx == 0,
72 | name: '首页',
73 | icon: SelfIcon.HomeFilling,
74 | press: () => changeNavbar(0),
75 | ),
76 | NavbarItem(
77 | isActive: navIdx == 1,
78 | name: '人脉',
79 | icon: SelfIcon.ContactFilling,
80 | num: 10,
81 | type: NavbarMessageType.dot,
82 | press: () => changeNavbar(1),
83 | ),
84 | NavbarItem(
85 | isActive: navIdx == 2,
86 | name: '机遇',
87 | num: 10,
88 | icon: SelfIcon.WorkFilling,
89 | press: () => changeNavbar(2),
90 | ),
91 | NavbarItem(
92 | isActive: navIdx == 3,
93 | name: '消息',
94 | num: 99,
95 | icon: SelfIcon.NotificationFilling,
96 | press: () => changeNavbar(3),
97 | ),
98 | NavbarItem(
99 | isActive: navIdx == 4,
100 | name: '我',
101 | icon: SelfIcon.UserFilling,
102 | press: () => changeNavbar(4),
103 | ),
104 | ],
105 | ),
106 | ), // This trailing comma makes auto-formatting nicer for build methods.
107 | );
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/lib/pages/chance/components/chance_notice.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/self_icon.dart';
3 | import 'package:flutter_maimai/constants.dart';
4 |
5 | class ChanceNotice extends StatelessWidget {
6 | const ChanceNotice({
7 | Key? key,
8 | required this.count,
9 | required this.press,
10 | }) : super(key: key);
11 |
12 | final int count;
13 | final VoidCallback press;
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return GestureDetector(
18 | onTap: press,
19 | child: Container(
20 | margin: EdgeInsets.only(
21 | left: iDefaultPadding,
22 | right: iDefaultPadding,
23 | bottom: iDefaultPadding,
24 | ),
25 | padding: EdgeInsets.all(iDefaultPadding),
26 | decoration: BoxDecoration(
27 | color: Colors.white,
28 | borderRadius: iBorderRadius,
29 | ),
30 | child: Row(
31 | children: [
32 | Container(
33 | width: 50,
34 | height: 50,
35 | decoration: BoxDecoration(
36 | color: iPrimaryColor.withOpacity(0.7),
37 | borderRadius: BorderRadius.all(
38 | Radius.circular(50),
39 | ),
40 | ),
41 | child: Icon(
42 | SelfIcon.NotificationFilling,
43 | size: 40,
44 | color: Colors.white,
45 | ),
46 | ),
47 | Expanded(
48 | flex: 1,
49 | child: Container(
50 | height: 50,
51 | child: Row(
52 | children: [
53 | Expanded(
54 | child: Padding(
55 | padding: EdgeInsets.symmetric(horizontal: iDefaultPadding / 2),
56 | child: Column(
57 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
58 | crossAxisAlignment: CrossAxisAlignment.start,
59 | children: [
60 | Text(
61 | '机会来了',
62 | style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
63 | ),
64 | Text(
65 | '又有$count家公司主动找你,想要聊聊,想要聊,想要聊聊',
66 | overflow: TextOverflow.ellipsis,
67 | maxLines: 1,
68 | style: TextStyle(fontSize: 14, color: Colors.black54),
69 | ),
70 | ],
71 | ),
72 | ),
73 | ),
74 | Container(
75 | height: 20,
76 | width: 20,
77 | child: Center(
78 | child: Text(
79 | '$count',
80 | textAlign: TextAlign.center,
81 | style: TextStyle(color: Colors.white, fontSize: 10),
82 | ),
83 | ),
84 | decoration: BoxDecoration(
85 | color: Colors.red,
86 | borderRadius: BorderRadius.all(Radius.circular(10)),
87 | ),
88 | ),
89 | ],
90 | ),
91 | ),
92 | ),
93 | Icon(
94 | SelfIcon.ArrowRight,
95 | size: 25,
96 | color: Colors.black12,
97 | ),
98 | ],
99 | ),
100 | ),
101 | );
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/lib/pages/contact/components/discover_more.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/self_icon.dart';
3 | import 'package:flutter_maimai/constants.dart';
4 | import 'package:flutter_maimai/mock/contact.dart';
5 |
6 | class DiscoverMore extends StatelessWidget {
7 | const DiscoverMore({Key? key}) : super(key: key);
8 |
9 | Widget _buildActionBtn() {
10 | return Row(
11 | mainAxisAlignment: MainAxisAlignment.end,
12 | children: [
13 | TextButton(
14 | onPressed: () {},
15 | child: Text(
16 | '再说吧',
17 | style: TextStyle(color: Colors.black45),
18 | ),
19 | style: ButtonStyle(
20 | backgroundColor: MaterialStateProperty.all(Colors.white),
21 | ),
22 | ),
23 | SizedBox(width: iDefaultPadding / 2),
24 | TextButton(
25 | onPressed: () {},
26 | child: Text(
27 | '去看看',
28 | style: TextStyle(color: Colors.white),
29 | ),
30 | style: ButtonStyle(
31 | backgroundColor: MaterialStateProperty.all(iPrimaryColor.withOpacity(0.9)),
32 | ),
33 | ),
34 | ],
35 | );
36 | }
37 |
38 | Widget _buildContentCard(Size size) {
39 | return Container(
40 | width: size.width - iDefaultPadding * 2,
41 | margin: EdgeInsets.only(top: iDefaultPadding / 2),
42 | padding: EdgeInsets.all(iDefaultPadding / 2),
43 | decoration: BoxDecoration(
44 | color: Color(0xFFF8F7FA),
45 | borderRadius: BorderRadius.all(Radius.circular(4)),
46 | ),
47 | child: Row(
48 | crossAxisAlignment: CrossAxisAlignment.start,
49 | children: [
50 | CircleAvatar(
51 | radius: 20,
52 | backgroundImage: NetworkImage(QinghuaLogo),
53 | ),
54 | SizedBox(width: 10),
55 | Container(
56 | width: size.width - iDefaultPadding * 3 - 50,
57 | child: Column(
58 | children: [
59 | Text(
60 | '我们为你推荐了一批你可能认识的同学,看看他们的职场近况吧',
61 | textAlign: TextAlign.left,
62 | softWrap: true,
63 | style: TextStyle(
64 | fontSize: 18,
65 | fontWeight: FontWeight.w500,
66 | ),
67 | ),
68 | Padding(
69 | padding: EdgeInsets.symmetric(vertical: iDefaultPadding / 5),
70 | child: Row(
71 | children: [
72 | Icon(
73 | SelfIcon.UserFilling,
74 | color: Colors.black26,
75 | size: 18,
76 | ),
77 | SizedBox(width: 5),
78 | Text(
79 | '张三、李四、王五、赵六',
80 | style: TextStyle(fontSize: 14, color: Colors.black38),
81 | ),
82 | ],
83 | ),
84 | ),
85 | _buildActionBtn(),
86 | ],
87 | ),
88 | ),
89 | ],
90 | ),
91 | );
92 | }
93 |
94 | @override
95 | Widget build(BuildContext context) {
96 | Size size = MediaQuery.of(context).size;
97 | return Container(
98 | width: size.width,
99 | color: Colors.white,
100 | padding: EdgeInsets.all(iDefaultPadding),
101 | child: Column(
102 | crossAxisAlignment: CrossAxisAlignment.start,
103 | children: [
104 | Text(
105 | '发现更多',
106 | style: TextStyle(fontSize: 16, color: Colors.black),
107 | ),
108 | _buildContentCard(size),
109 | ],
110 | ),
111 | );
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/lib/pages/contact/contact.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/common/common.dart';
3 | import 'package:flutter_maimai/components/app_bar_action.dart';
4 | import 'package:flutter_maimai/components/customer_tab_item.dart';
5 | import 'package:flutter_maimai/components/search_field.dart';
6 | import 'package:flutter_maimai/components/self_icon.dart';
7 | import 'package:flutter_maimai/constants.dart';
8 | import 'package:flutter_maimai/mock/contact.dart';
9 | import 'package:flutter_maimai/pages/contact/components/contact_item.dart';
10 | import 'package:flutter_maimai/pages/contact/components/contact_menu.dart';
11 | import 'package:flutter_maimai/pages/contact/components/discover_more.dart';
12 | import 'package:flutter_maimai/pages/contact/components/friend_msg.dart';
13 | import 'package:flutter_maimai/pages/contact/components/request_list.dart';
14 |
15 | class Contact extends StatefulWidget {
16 | const Contact({Key? key}) : super(key: key);
17 |
18 | @override
19 | _ContactState createState() => _ContactState();
20 | }
21 |
22 | class _ContactState extends State {
23 | List contactList = ContactMockList;
24 |
25 | AppBar _buildAppBar() {
26 | return AppBar(
27 | centerTitle: true,
28 | title: Text('人脉', style: iAppBarTextStyle),
29 | actions: [
30 | AppBarAction(icon: SelfIcon.User, press: () {}, right: iDefaultPadding * 0.8),
31 | ],
32 | shadowColor: Colors.transparent,
33 | backgroundColor: iAppBarBackgroundColor,
34 | );
35 | }
36 |
37 | Widget _buildTop() {
38 | return SliverToBoxAdapter(
39 | child: Column(
40 | children: [
41 | SearchField(
42 | placeholder: '找同学:XXX大学 XX专业',
43 | ),
44 | ContactMenu(),
45 | FriendMsg(),
46 | RequestList(),
47 | DiscoverMore(),
48 | ],
49 | ),
50 | );
51 | }
52 |
53 | Widget _buildTabContent() {
54 | return Row(
55 | children: [
56 | CustomerTabItem(title: '推荐', active: true, press: () {}),
57 | SizedBox(width: iDefaultPadding),
58 | CustomerTabItem(title: '同事', active: false, press: () {}),
59 | SizedBox(width: iDefaultPadding),
60 | CustomerTabItem(title: '校友', active: false, press: () {}),
61 | SizedBox(width: iDefaultPadding),
62 | CustomerTabItem(title: '同乡', active: false, press: () {}),
63 | SizedBox(width: iDefaultPadding),
64 | CustomerTabItem(title: '同行', active: false, press: () {}),
65 | ],
66 | );
67 | }
68 |
69 | Widget _buildStickBar() {
70 | return SliverPersistentHeader(
71 | pinned: true, //是否固定在顶部
72 | floating: true,
73 | delegate: CustomSliverDelegate(
74 | minHeight: 50, //收起的高度
75 | maxHeight: 50, //展开的最大高度
76 | child: Container(
77 | padding: EdgeInsets.symmetric(horizontal: iDefaultPadding),
78 | color: Colors.white,
79 | alignment: Alignment.centerLeft,
80 | child: _buildTabContent(),
81 | ),
82 | ),
83 | );
84 | }
85 |
86 | Widget _buildList() {
87 | return SliverList(
88 | delegate: SliverChildBuilderDelegate(
89 | (context, index) {
90 | return ContactItem(
91 | contact: contactList[index],
92 | press: () {},
93 | );
94 | },
95 | childCount: contactList.length,
96 | ),
97 | );
98 | }
99 |
100 | @override
101 | Widget build(BuildContext context) {
102 | Size size = MediaQuery.of(context).size;
103 | return Scaffold(
104 | backgroundColor: iAppBackgroundColor,
105 | appBar: _buildAppBar(),
106 | body: CustomScrollView(
107 | slivers: [
108 | _buildTop(),
109 | _buildStickBar(),
110 | _buildList(),
111 | SliverPadding(
112 | padding: EdgeInsets.only(bottom: 200),
113 | ),
114 | ],
115 | ),
116 | );
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/lib/pages/chance/chance.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/common/common.dart';
3 | import 'package:flutter_maimai/components/app_ad.dart';
4 | import 'package:flutter_maimai/components/app_bar_action.dart';
5 | import 'package:flutter_maimai/components/customer_tab_item.dart';
6 | import 'package:flutter_maimai/components/search_field.dart';
7 | import 'package:flutter_maimai/components/self_icon.dart';
8 | import 'package:flutter_maimai/constants.dart';
9 | import 'package:flutter_maimai/mock/ad.dart';
10 | import 'package:flutter_maimai/mock/company.dart';
11 | import 'package:flutter_maimai/pages/chance/components/chance_notice.dart';
12 | import 'package:flutter_maimai/pages/chance/components/follow_market.dart';
13 | import 'package:flutter_maimai/pages/chance/components/post_item.dart';
14 |
15 | class Chance extends StatefulWidget {
16 | const Chance({Key? key}) : super(key: key);
17 |
18 | @override
19 | _ChanceState createState() => _ChanceState();
20 | }
21 |
22 | class _ChanceState extends State {
23 | List postList = CompanyPostMockList;
24 |
25 | AppBar _buildAppBar() {
26 | return AppBar(
27 | centerTitle: true,
28 | title: Text('看机会', style: iAppBarTextStyle),
29 | actions: [
30 | AppBarAction(icon: SelfIcon.Add, press: () {}, right: iDefaultPadding * 0.8),
31 | ],
32 | shadowColor: Colors.transparent,
33 | backgroundColor: iAppBarBackgroundColor,
34 | );
35 | }
36 |
37 | Widget _buildTop() {
38 | return SliverToBoxAdapter(
39 | child: Column(
40 | children: [
41 | SearchField(
42 | haveButton: true,
43 | placeholder: '搜索感兴趣的职位',
44 | btnText: '搜索',
45 | ),
46 | ImageAd(ChanceImageAdUrl),
47 | FollowMarket(),
48 | ChanceNotice(
49 | count: 1,
50 | press: () {},
51 | ),
52 | ],
53 | ),
54 | );
55 | }
56 |
57 | Widget _buildTabContent() {
58 | return Row(
59 | children: [
60 | CustomerTabItem(title: '推荐', active: true, press: () {}, haveBorder: true),
61 | SizedBox(width: iDefaultPadding),
62 | CustomerTabItem(title: '最新', active: false, press: () {}, haveBadge: true, haveBorder: true),
63 | Spacer(),
64 | Row(
65 | children: [
66 | Icon(
67 | SelfIcon.EditFilling,
68 | size: 16,
69 | ),
70 | Text(
71 | '职位偏好',
72 | style: TextStyle(
73 | fontSize: 14,
74 | fontWeight: FontWeight.w500,
75 | ),
76 | )
77 | ],
78 | ),
79 | ],
80 | );
81 | }
82 |
83 | Widget _buildStickBar() {
84 | return SliverPersistentHeader(
85 | pinned: true, //是否固定在顶部
86 | floating: true,
87 | delegate: CustomSliverDelegate(
88 | minHeight: 50, //收起的高度
89 | maxHeight: 50, //展开的最大高度
90 | child: Container(
91 | padding: EdgeInsets.symmetric(horizontal: iDefaultPadding),
92 | color: Colors.white,
93 | alignment: Alignment.centerLeft,
94 | child: _buildTabContent(),
95 | ),
96 | ),
97 | );
98 | }
99 |
100 | Widget _buildList() {
101 | return SliverList(
102 | delegate: SliverChildBuilderDelegate(
103 | (context, index) {
104 | return PostItem(
105 | itemInfo: postList[index],
106 | press: () {},
107 | );
108 | },
109 | childCount: postList.length,
110 | ),
111 | );
112 | }
113 |
114 | @override
115 | Widget build(BuildContext context) {
116 | return Scaffold(
117 | backgroundColor: iAppBackgroundColor,
118 | appBar: _buildAppBar(),
119 | body: CustomScrollView(
120 | slivers: [
121 | _buildTop(),
122 | _buildStickBar(),
123 | _buildList(),
124 | SliverPadding(
125 | padding: EdgeInsets.only(bottom: 200),
126 | ),
127 | ],
128 | ),
129 | );
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/lib/pages/chance/components/post_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/self_icon.dart';
3 | import 'package:flutter_maimai/components/tag.dart';
4 | import 'package:flutter_maimai/constants.dart';
5 |
6 | class PostItem extends StatelessWidget {
7 | const PostItem({
8 | Key? key,
9 | required this.itemInfo,
10 | required this.press,
11 | }) : super(key: key);
12 |
13 | final itemInfo;
14 | final VoidCallback press;
15 |
16 | String _dealSalaryText() {
17 | var obj = itemInfo['salary'];
18 | if (obj == null) return '';
19 | int minNum = obj['min'];
20 | int maxNum = obj['max'];
21 | int monthNum = itemInfo['month'];
22 | return '$minNum' + 'k-' + '$maxNum' + 'k·$monthNum薪';
23 | }
24 |
25 | Widget _buildLogo() {
26 | return Container(
27 | width: 60,
28 | height: 60,
29 | decoration: BoxDecoration(
30 | borderRadius: iBorderRadius,
31 | border: new Border.all(
32 | color: Colors.black12,
33 | ),
34 | image: new DecorationImage(
35 | image: new NetworkImage(itemInfo['companyLogo']),
36 | ),
37 | ),
38 | );
39 | }
40 |
41 | Widget _buildHeader() {
42 | return Row(
43 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
44 | children: [
45 | Text(
46 | itemInfo['postName'],
47 | style: TextStyle(
48 | color: Colors.black,
49 | fontSize: 18,
50 | fontWeight: FontWeight.bold,
51 | ),
52 | ),
53 | // Spacer(flex: 1,),
54 | Text(
55 | _dealSalaryText(),
56 | style: TextStyle(
57 | color: iPrimaryColor,
58 | fontSize: 18,
59 | fontWeight: FontWeight.bold,
60 | ),
61 | ),
62 | ],
63 | );
64 | }
65 |
66 | Widget _buildTagList() {
67 | return Container(
68 | margin: EdgeInsets.symmetric(vertical: 10),
69 | child: Row(
70 | children: [
71 | Tag(itemInfo['location']),
72 | SizedBox(width: 10),
73 | Tag(itemInfo['education']),
74 | SizedBox(width: 10),
75 | Tag(itemInfo['workYear']),
76 | ],
77 | ),
78 | );
79 | }
80 |
81 | Widget _buildExtInfo() {
82 | TextStyle _style = TextStyle(
83 | color: Colors.black45,
84 | fontSize: 14,
85 | fontWeight: FontWeight.w500,
86 | );
87 | return Row(
88 | children: [
89 | Text(itemInfo['companyName'], style: _style),
90 | SizedBox(width: 5),
91 | Text(itemInfo['status'], style: _style),
92 | SizedBox(width: 5),
93 | Text(itemInfo['people'], style: _style),
94 | Spacer(),
95 | InkWell(
96 | onTap: press,
97 | child: Icon(
98 | SelfIcon.Close,
99 | size: 15,
100 | color: Colors.black26,
101 | ),
102 | ),
103 | ],
104 | );
105 | }
106 |
107 | Widget _buildContent() {
108 | return Expanded(
109 | child: Container(
110 | margin: EdgeInsets.only(left: iDefaultPadding / 2),
111 | child: Column(
112 | children: [
113 | _buildHeader(),
114 | _buildTagList(),
115 | _buildExtInfo(),
116 | ],
117 | ),
118 | ),
119 | );
120 | }
121 |
122 | @override
123 | Widget build(BuildContext context) {
124 | // print("数据为:$itemInfo.pay['pay']");
125 | return Container(
126 | decoration: BoxDecoration(
127 | color: Colors.white,
128 | ),
129 | padding: EdgeInsets.symmetric(horizontal: iDefaultPadding),
130 | child: Container(
131 | padding: EdgeInsets.symmetric(vertical: iDefaultPadding),
132 | decoration: BoxDecoration(
133 | border: new Border(
134 | bottom: BorderSide(
135 | color: Colors.black.withOpacity(0.05),
136 | ),
137 | ),
138 | ),
139 | child: Row(
140 | crossAxisAlignment: CrossAxisAlignment.start,
141 | children: [
142 | _buildLogo(),
143 | _buildContent(),
144 | ],
145 | ),
146 | ),
147 | );
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/lib/pages/owner/owner.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/app_bar_action.dart';
3 | import 'package:flutter_maimai/components/card_container.dart';
4 | import 'package:flutter_maimai/components/column_menu_item.dart';
5 | import 'package:flutter_maimai/components/safe_area.dart';
6 | import 'package:flutter_maimai/components/self_icon.dart';
7 | import 'package:flutter_maimai/constants.dart';
8 | import 'package:flutter_maimai/pages/owner/components/owner_info.dart';
9 | import 'package:flutter_maimai/pages/owner/components/owner_menu.dart';
10 |
11 | class Owner extends StatelessWidget {
12 | const Owner({Key? key}) : super(key: key);
13 |
14 | AppBar _buildAppBar() {
15 | return AppBar(
16 | actions: [
17 | AppBarAction(
18 | icon: SelfIcon.Scanning,
19 | press: () {},
20 | right: iDefaultPadding * 0.8,
21 | ),
22 | ],
23 | shadowColor: Colors.transparent,
24 | backgroundColor: iAppBarBackgroundColor,
25 | );
26 | }
27 |
28 | Widget _buildMenuList() {
29 | TextStyle _style = TextStyle(fontSize: 14, color: Colors.black38);
30 | TextStyle _styleYellow = TextStyle(fontSize: 14, color: Colors.yellow[700]);
31 | return CardContainer(
32 | padding: EdgeInsets.symmetric(horizontal: iDefaultPadding),
33 | child: Column(
34 | children: [
35 | ColumnMenuItem(
36 | value: '我的社区身份',
37 | press: () {},
38 | ),
39 | ColumnMenuItem(
40 | value: '我的收藏',
41 | press: () {},
42 | ),
43 | ColumnMenuItem(
44 | value: '关注内容',
45 | press: () {},
46 | ),
47 | ColumnMenuItem(
48 | value: '历史通知',
49 | press: () {},
50 | ),
51 | ColumnMenuItem(
52 | value: '成为认证用户',
53 | press: () {},
54 | ext: Text('完善资料', style: _style),
55 | ),
56 | ColumnMenuItem(
57 | value: '成为会员',
58 | press: () {},
59 | ext: Text('限时免费使用', style: _styleYellow),
60 | ),
61 | ColumnMenuItem(
62 | value: '附件简历',
63 | press: () {},
64 | ext: Text('去上传', style: _style),
65 | ),
66 | ColumnMenuItem(
67 | value: '加好友额度中心',
68 | press: () {},
69 | ext: Text('本月待领取0', style: _style),
70 | ),
71 | ColumnMenuItem(
72 | value: '钱包',
73 | press: () {},
74 | ext: Text('查看额度', style: _style),
75 | ),
76 | ColumnMenuItem(
77 | value: '人气周报',
78 | press: () {},
79 | ext: Text('相比上周下降了27%', style: _style),
80 | ),
81 | ColumnMenuItem(
82 | value: '职趣实验室',
83 | press: () {},
84 | ),
85 | ColumnMenuItem(
86 | value: '职场福利',
87 | press: () {},
88 | ),
89 | ],
90 | ),
91 | );
92 | }
93 |
94 | Widget _buildSettingMenuList() {
95 | return CardContainer(
96 | padding: EdgeInsets.symmetric(horizontal: iDefaultPadding),
97 | child: Column(
98 | children: [
99 | ColumnMenuItem(
100 | value: '意见反馈',
101 | press: () {},
102 | ),
103 | ColumnMenuItem(
104 | value: '在线客服',
105 | press: () {},
106 | ),
107 | ColumnMenuItem(
108 | value: '隐私',
109 | press: () {},
110 | ),
111 | ColumnMenuItem(
112 | value: '第三方SDK列表',
113 | press: () {},
114 | ),
115 | ColumnMenuItem(
116 | value: '设置',
117 | press: () {},
118 | ),
119 | ],
120 | ),
121 | );
122 | }
123 |
124 | @override
125 | Widget build(BuildContext context) {
126 | return Scaffold(
127 | backgroundColor: iAppBackgroundColor,
128 | appBar: _buildAppBar(),
129 | body: SingleChildScrollView(
130 | child: Column(
131 | children: [
132 | OwnerInfo(),
133 | OwnerMenu(),
134 | _buildMenuList(),
135 | _buildSettingMenuList(),
136 | SafeAreaBottom(),
137 | ],
138 | ),
139 | ),
140 | );
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/lib/pages/home/components/hot_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/self_icon.dart';
3 | import 'package:flutter_maimai/constants.dart';
4 | import 'package:flutter_maimai/mock/home.dart';
5 |
6 | class HotList extends StatefulWidget {
7 | const HotList({Key? key}) : super(key: key);
8 |
9 | @override
10 | _HotListState createState() => _HotListState();
11 | }
12 |
13 | class _HotListState extends State {
14 | List hotList = HotListMock;
15 |
16 | Widget _buildTitle() {
17 | return Row(
18 | children: [
19 | Text(
20 | '脉脉',
21 | style: TextStyle(fontSize: 25, color: iPrimaryColor, fontWeight: FontWeight.w900),
22 | ),
23 | Text(
24 | '热榜',
25 | style: TextStyle(fontSize: 25, color: Colors.black, fontWeight: FontWeight.w900),
26 | ),
27 | ],
28 | );
29 | }
30 |
31 | Widget _buildListContent() {
32 | List _list = [];
33 | for (int i = 0; i < hotList.length; i++) {
34 | _list.add(HotListItem(item: hotList[i], idx: i + 1));
35 | }
36 | return Column(
37 | children: _list,
38 | );
39 | }
40 |
41 | Widget _buildMoreAction() {
42 | return Padding(
43 | padding: EdgeInsets.symmetric(vertical: iDefaultPadding / 2),
44 | child: Row(
45 | mainAxisAlignment: MainAxisAlignment.center,
46 | crossAxisAlignment: CrossAxisAlignment.center,
47 | children: [
48 | Text('查看更多', style: TextStyle(color: iPrimaryColor, fontSize: 14)),
49 | Icon(
50 | SelfIcon.ArrowRight,
51 | size: 14,
52 | ),
53 | ],
54 | ),
55 | );
56 | }
57 |
58 | @override
59 | Widget build(BuildContext context) {
60 | return Container(
61 | margin: EdgeInsets.symmetric(vertical: iDefaultPadding / 2, horizontal: iDefaultPadding),
62 | padding: EdgeInsets.all(iDefaultPadding / 1.5),
63 | decoration: BoxDecoration(
64 | color: iAppBackgroundColor,
65 | borderRadius: iBorderRadius,
66 | ),
67 | child: Column(
68 | children: [
69 | _buildTitle(),
70 | _buildListContent(),
71 | GestureDetector(
72 | onTap: () {},
73 | child: _buildMoreAction(),
74 | ),
75 | ],
76 | ),
77 | );
78 | }
79 | }
80 |
81 | class HotListItem extends StatelessWidget {
82 | const HotListItem({
83 | Key? key,
84 | required this.item,
85 | required this.idx,
86 | }) : super(key: key);
87 |
88 | final item;
89 | final int idx;
90 |
91 | Widget _buildTag() {
92 | bool _isNew = item['isNew'];
93 | Color _color = _isNew ? iPrimaryColor : Colors.red;
94 | return Container(
95 | height: 20,
96 | width: 20,
97 | decoration: BoxDecoration(
98 | borderRadius: BorderRadius.all(Radius.circular(4)),
99 | border: new Border.all(width: 1, color: _color),
100 | ),
101 | child: Center(
102 | child: Text(
103 | _isNew ? '新' : '爆',
104 | style: TextStyle(color: _color, fontSize: 12),
105 | ),
106 | ),
107 | );
108 | }
109 |
110 | Widget _buildIdx() {
111 | Color _color = Colors.black;
112 | switch (idx) {
113 | case 1:
114 | _color = Colors.red[500] as Color;
115 | break;
116 | case 2:
117 | _color = Colors.yellow[900] as Color;
118 | break;
119 | case 3:
120 | _color = Colors.yellow[700] as Color;
121 | break;
122 | }
123 | return Container(
124 | width: 16,
125 | height: 16,
126 | decoration: BoxDecoration(
127 | color: _color,
128 | borderRadius: BorderRadius.all(Radius.circular(3)),
129 | ),
130 | child: Center(
131 | child: Text(
132 | '$idx',
133 | style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w500),
134 | ),
135 | ),
136 | );
137 | }
138 |
139 | @override
140 | Widget build(BuildContext context) {
141 | return GestureDetector(
142 | onTap: () {},
143 | child: Container(
144 | margin: EdgeInsets.only(top: iDefaultPadding / 4),
145 | child: Row(
146 | children: [
147 | _buildIdx(),
148 | Expanded(
149 | child: Padding(
150 | padding: EdgeInsets.symmetric(horizontal: 5),
151 | child: Text(
152 | item['name'],
153 | overflow: TextOverflow.ellipsis,
154 | style: TextStyle(fontSize: 16, color: Colors.black),
155 | ),
156 | ),
157 | ),
158 | _buildTag(),
159 | ],
160 | ),
161 | ),
162 | );
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/lib/main copy.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/navbar_item.dart';
3 | import 'package:flutter_maimai/components/self_icon.dart';
4 | import 'package:flutter_maimai/constants.dart';
5 | import 'package:flutter_maimai/enums.dart';
6 | import 'package:flutter_maimai/pages/chance/chance.dart';
7 | import 'package:flutter_maimai/pages/contact/contact.dart';
8 | import 'package:flutter_maimai/pages/home/home.dart';
9 | import 'package:flutter_maimai/pages/message/message.dart';
10 | import 'package:flutter_maimai/pages/owner/owner.dart';
11 |
12 | void main() {
13 | runApp(MyApp());
14 | }
15 |
16 | class MyApp extends StatelessWidget {
17 | @override
18 | Widget build(BuildContext context) {
19 | return MaterialApp(
20 | title: 'Flutter Maimai',
21 | debugShowCheckedModeBanner: false,
22 | home: MyHomePage(),
23 | );
24 | }
25 | }
26 |
27 | class MyHomePage extends StatefulWidget {
28 | MyHomePage({Key? key}) : super(key: key);
29 |
30 | @override
31 | _MyHomePageState createState() => _MyHomePageState();
32 | }
33 |
34 | class _MyHomePageState extends State {
35 | int navIdx = 0;
36 | List pageList = [
37 | Home(),
38 | // Contact(),
39 | // StickyPage(),
40 | Chance(),
41 | Message(),
42 | Owner(),
43 | ];
44 |
45 | void changeNavbar(int idx) {
46 | setState(() {
47 | navIdx = idx;
48 | });
49 | }
50 |
51 | @override
52 | Widget build(BuildContext context) {
53 | Size size = MediaQuery.of(context).size;
54 | return Scaffold(
55 | body: pageList[navIdx],
56 | backgroundColor: iAppBackgroundColor,
57 | floatingActionButtonLocation: CustomFloatingActionButtonLocation(FloatingActionButtonLocation.centerDocked, 0, 50),
58 | floatingActionButton: Container(
59 | width: size.width,
60 | height: 100,
61 | padding: EdgeInsets.only(
62 | left: iDefaultPadding,
63 | right: iDefaultPadding,
64 | ),
65 | decoration: BoxDecoration(
66 | gradient: LinearGradient(
67 | begin: Alignment.topCenter,
68 | end: Alignment.bottomCenter,
69 | colors: [
70 | Colors.white.withOpacity(0.2),
71 | Colors.white.withOpacity(0.8),
72 | ],
73 | ),
74 | color: Colors.white.withOpacity(0.8),
75 | ),
76 | child: Stack(
77 | children: [
78 | Container(
79 | width: size.width * 0.9,
80 | height: iNavBarHeight,
81 | decoration: BoxDecoration(
82 | color: iAppBottomBarColor,
83 | borderRadius: BorderRadius.circular(20),
84 | ),
85 | child: Row(
86 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
87 | children: [
88 | NavbarItem(
89 | isActive: navIdx == 0,
90 | name: '首页',
91 | icon: SelfIcon.HomeFilling,
92 | press: () => changeNavbar(0),
93 | ),
94 | NavbarItem(
95 | isActive: navIdx == 1,
96 | name: '人脉',
97 | icon: SelfIcon.ContactFilling,
98 | num: 10,
99 | type: NavbarMessageType.dot,
100 | press: () => changeNavbar(1),
101 | ),
102 | NavbarItem(
103 | isActive: navIdx == 2,
104 | name: '机遇',
105 | num: 10,
106 | icon: SelfIcon.WorkFilling,
107 | press: () => changeNavbar(2),
108 | ),
109 | NavbarItem(
110 | isActive: navIdx == 3,
111 | name: '消息',
112 | num: 99,
113 | icon: SelfIcon.NotificationFilling,
114 | press: () => changeNavbar(3),
115 | ),
116 | NavbarItem(
117 | isActive: navIdx == 4,
118 | name: '我',
119 | icon: SelfIcon.UserFilling,
120 | press: () => changeNavbar(4),
121 | ),
122 | ],
123 | ),
124 | ),
125 | ],
126 | ),
127 | ),
128 | );
129 | }
130 | }
131 |
132 | class CustomFloatingActionButtonLocation extends FloatingActionButtonLocation {
133 | FloatingActionButtonLocation location;
134 | double offsetX; // X方向的偏移量
135 | double offsetY; // Y方向的偏移量
136 | CustomFloatingActionButtonLocation(this.location, this.offsetX, this.offsetY);
137 |
138 | @override
139 | Offset getOffset(ScaffoldPrelayoutGeometry scaffoldGeometry) {
140 | Offset offset = location.getOffset(scaffoldGeometry);
141 | return Offset(offset.dx + offsetX, offset.dy + offsetY);
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/lib/pages/message/message.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/components/app_bar_action.dart';
3 | import 'package:flutter_maimai/components/self_icon.dart';
4 | import 'package:flutter_maimai/constants.dart';
5 | import 'package:flutter_maimai/mock/message.dart';
6 | import 'package:flutter_maimai/pages/message/components/message_item.dart';
7 | import 'package:flutter_maimai/pages/message/components/message_type.dart';
8 |
9 | class Message extends StatefulWidget {
10 | const Message({Key? key}) : super(key: key);
11 |
12 | @override
13 | _MessageState createState() => _MessageState();
14 | }
15 |
16 | class _MessageState extends State {
17 | List messageList = MessageMockList;
18 |
19 | AppBar _buildAppBar() {
20 | return AppBar(
21 | centerTitle: true,
22 | title: Text('消息', style: iAppBarTextStyle),
23 | actions: [
24 | AppBarAction(
25 | icon: SelfIcon.Add,
26 | right: iDefaultPadding * 0.8,
27 | press: () {},
28 | ),
29 | ],
30 | leading: AppBarAction(
31 | icon: SelfIcon.Search,
32 | left: iDefaultPadding * 0.6,
33 | press: () {},
34 | ),
35 | shadowColor: Colors.transparent,
36 | backgroundColor: iAppBarBackgroundColor,
37 | );
38 | }
39 |
40 | Widget _buildModalHeader(String title) {
41 | return Container(
42 | height: iModalHeaderHeight,
43 | child: Row(
44 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
45 | children: [
46 | GestureDetector(
47 | onTap: () {
48 | Navigator.pop(context);
49 | },
50 | child: SizedBox(
51 | width: iModalHeaderHeight,
52 | height: iModalHeaderHeight,
53 | child: Center(
54 | child: Icon(SelfIcon.CloseBold, size: 20),
55 | ),
56 | ),
57 | ),
58 | Expanded(
59 | flex: 1,
60 | child: Center(
61 | child: Text(
62 | title,
63 | style: TextStyle(fontSize: 18, color: Colors.black, fontWeight: FontWeight.w500),
64 | ),
65 | ),
66 | ),
67 | SizedBox(width: iModalHeaderHeight, height: iModalHeaderHeight),
68 | ],
69 | ),
70 | );
71 | }
72 |
73 | Widget _buildModelSheetContent(Size size, String title) {
74 | Radius _radius = Radius.circular(20);
75 | return Container(
76 | height: size.height * 0.88,
77 | decoration: BoxDecoration(
78 | color: Colors.white,
79 | borderRadius: BorderRadius.only(topLeft: _radius, topRight: _radius),
80 | ),
81 | child: Column(
82 | children: [
83 | _buildModalHeader(title),
84 | Expanded(
85 | child: ListView.builder(
86 | itemBuilder: (context, index) {
87 | return MessageItem(message: messageList[index], press: () {});
88 | },
89 | itemCount: messageList.length,
90 | ),
91 | ),
92 | ],
93 | ),
94 | );
95 | }
96 |
97 | void _showModalSheetAction(Size size, String title) {
98 | showModalBottomSheet(
99 | isDismissible: false,
100 | isScrollControlled: true,
101 | context: context,
102 | elevation: 10,
103 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
104 | builder: (BuildContext context) {
105 | return _buildModelSheetContent(size, title);
106 | },
107 | );
108 | }
109 |
110 | Widget _buildMessageType(Size size) {
111 | return SliverToBoxAdapter(
112 | child: Container(
113 | color: iAppBackgroundColor,
114 | padding: EdgeInsets.symmetric(horizontal: iDefaultPadding, vertical: iDefaultPadding / 2),
115 | child: Row(
116 | children: [
117 | MessageType(
118 | text: '我主动沟通',
119 | press: () {
120 | _showModalSheetAction(size, '我主动沟通');
121 | },
122 | ),
123 | MessageType(
124 | text: '只看未读',
125 | press: () {
126 | _showModalSheetAction(size, '只看未读');
127 | },
128 | ),
129 | MessageType(
130 | text: '好友消息',
131 | press: () {
132 | _showModalSheetAction(size, '好友消息');
133 | },
134 | ),
135 | ],
136 | ),
137 | ),
138 | );
139 | }
140 |
141 | Widget _buildMessageList() {
142 | return SliverList(
143 | delegate: SliverChildBuilderDelegate(
144 | (context, index) {
145 | return MessageItem(message: messageList[index], press: () {});
146 | },
147 | childCount: messageList.length,
148 | ),
149 | );
150 | }
151 |
152 | @override
153 | Widget build(BuildContext context) {
154 | Size size = MediaQuery.of(context).size;
155 | return Scaffold(
156 | backgroundColor: iAppBackgroundColor,
157 | appBar: _buildAppBar(),
158 | body: CustomScrollView(
159 | slivers: [
160 | _buildMessageType(size),
161 | _buildMessageList(),
162 | ],
163 | ),
164 | );
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/lib/mock/company.dart:
--------------------------------------------------------------------------------
1 | List CompanyMockList = [{}];
2 |
3 | List CompanyPostMockList = [
4 | {
5 | "postId": 1,
6 | "companyName": "公司A名称",
7 | "companyLogo":
8 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202107%2F27%2F20210727200514_f11e2.jpeg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1678447184&t=88f3f49fe9cdaa106da0ea9bd98b8f95",
9 | "postName": "web前端工程师",
10 | "salary": {
11 | "min": 12,
12 | "max": 24,
13 | },
14 | "month": 13,
15 | "status": "不需要融资",
16 | "people": "1000-5000人",
17 | "location": "杭州",
18 | "education": "本科及以上",
19 | "workYear": "5-10年"
20 | },
21 | {
22 | "postId": 2,
23 | "companyName": "公司B名称",
24 | "companyLogo": "https://img1.baidu.com/it/u=642615975,3013253527&fm=253&fmt=auto&app=138&f=JPEG?w=501&h=500",
25 | "postName": "高级前端工程师",
26 | "salary": {
27 | "min": 20,
28 | "max": 40,
29 | },
30 | "month": 14,
31 | "status": "A轮",
32 | "people": "10-50人",
33 | "location": "杭州",
34 | "education": "本科及以上",
35 | "workYear": "3-5年"
36 | },
37 | {
38 | "postId": 1,
39 | "companyName": "公司A名称",
40 | "companyLogo":
41 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202107%2F27%2F20210727200514_f11e2.jpeg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1678447184&t=88f3f49fe9cdaa106da0ea9bd98b8f95",
42 | "postName": "Web前端开发工程师",
43 | "salary": {
44 | "min": 12,
45 | "max": 24,
46 | },
47 | "month": 13,
48 | "status": "不需要融资",
49 | "people": "1000-5000人",
50 | "location": "杭州",
51 | "education": "本科及以上",
52 | "workYear": "5-10年"
53 | },
54 | {
55 | "postId": 2,
56 | "companyName": "公司B名称",
57 | "companyLogo": "https://img1.baidu.com/it/u=642615975,3013253527&fm=253&fmt=auto&app=138&f=JPEG?w=501&h=500",
58 | "postName": "高级前端工程师",
59 | "salary": {
60 | "min": 20,
61 | "max": 40,
62 | },
63 | "month": 14,
64 | "status": "A轮",
65 | "people": "10-50人",
66 | "location": "杭州",
67 | "education": "本科及以上",
68 | "workYear": "3-5年"
69 | },
70 | {
71 | "postId": 1,
72 | "companyName": "公司A名称",
73 | "companyLogo":
74 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202107%2F27%2F20210727200514_f11e2.jpeg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1678447184&t=88f3f49fe9cdaa106da0ea9bd98b8f95",
75 | "postName": "Web前端开发工程师",
76 | "salary": {
77 | "min": 12,
78 | "max": 24,
79 | },
80 | "month": 13,
81 | "status": "不需要融资",
82 | "people": "1000-5000人",
83 | "location": "杭州",
84 | "education": "本科及以上",
85 | "workYear": "5-10年"
86 | },
87 | {
88 | "postId": 2,
89 | "companyName": "公司B名称",
90 | "companyLogo": "https://img1.baidu.com/it/u=642615975,3013253527&fm=253&fmt=auto&app=138&f=JPEG?w=501&h=500",
91 | "postName": "高级前端工程师",
92 | "salary": {
93 | "min": 20,
94 | "max": 40,
95 | },
96 | "month": 14,
97 | "status": "A轮",
98 | "people": "10-50人",
99 | "location": "杭州",
100 | "education": "本科及以上",
101 | "workYear": "3-5年"
102 | },
103 | {
104 | "postId": 1,
105 | "companyName": "公司A名称",
106 | "companyLogo":
107 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202107%2F27%2F20210727200514_f11e2.jpeg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1678447184&t=88f3f49fe9cdaa106da0ea9bd98b8f95",
108 | "postName": "Web前端开发工程师",
109 | "salary": {
110 | "min": 12,
111 | "max": 24,
112 | },
113 | "month": 13,
114 | "status": "不需要融资",
115 | "people": "1000-5000人",
116 | "location": "杭州",
117 | "education": "本科及以上",
118 | "workYear": "5-10年"
119 | },
120 | {
121 | "postId": 2,
122 | "companyName": "公司B名称",
123 | "companyLogo": "https://img1.baidu.com/it/u=642615975,3013253527&fm=253&fmt=auto&app=138&f=JPEG?w=501&h=500",
124 | "postName": "高级前端工程师",
125 | "salary": {
126 | "min": 20,
127 | "max": 40,
128 | },
129 | "month": 14,
130 | "status": "A轮",
131 | "people": "10-50人",
132 | "location": "杭州",
133 | "education": "本科及以上",
134 | "workYear": "3-5年"
135 | },
136 | {
137 | "postId": 1,
138 | "companyName": "公司A名称",
139 | "companyLogo":
140 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202107%2F27%2F20210727200514_f11e2.jpeg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1678447184&t=88f3f49fe9cdaa106da0ea9bd98b8f95",
141 | "postName": "Web前端开发工程师",
142 | "salary": {
143 | "min": 12,
144 | "max": 24,
145 | },
146 | "month": 13,
147 | "status": "不需要融资",
148 | "people": "1000-5000人",
149 | "location": "杭州",
150 | "education": "本科及以上",
151 | "workYear": "5-10年"
152 | },
153 | {
154 | "postId": 2,
155 | "companyName": "公司B名称",
156 | "companyLogo": "https://img1.baidu.com/it/u=642615975,3013253527&fm=253&fmt=auto&app=138&f=JPEG?w=501&h=500",
157 | "postName": "高级前端工程师",
158 | "salary": {
159 | "min": 20,
160 | "max": 40,
161 | },
162 | "month": 14,
163 | "status": "A轮",
164 | "people": "10-50人",
165 | "location": "杭州",
166 | "education": "本科及以上",
167 | "workYear": "3-5年"
168 | },
169 | {
170 | "postId": 1,
171 | "companyName": "公司A名称",
172 | "companyLogo":
173 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202107%2F27%2F20210727200514_f11e2.jpeg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1678447184&t=88f3f49fe9cdaa106da0ea9bd98b8f95",
174 | "postName": "Web前端开发工程师",
175 | "salary": {
176 | "min": 12,
177 | "max": 24,
178 | },
179 | "month": 13,
180 | "status": "不需要融资",
181 | "people": "1000-5000人",
182 | "location": "杭州",
183 | "education": "本科及以上",
184 | "workYear": "5-10年"
185 | },
186 | {
187 | "postId": 2,
188 | "companyName": "公司B名称",
189 | "companyLogo": "https://img1.baidu.com/it/u=642615975,3013253527&fm=253&fmt=auto&app=138&f=JPEG?w=501&h=500",
190 | "postName": "高级前端工程师",
191 | "salary": {
192 | "min": 20,
193 | "max": 40,
194 | },
195 | "month": 14,
196 | "status": "A轮",
197 | "people": "10-50人",
198 | "location": "杭州",
199 | "education": "本科及以上",
200 | "workYear": "3-5年"
201 | },
202 | ];
203 |
--------------------------------------------------------------------------------
/lib/pages/home/home.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_maimai/common/customer_tab_size_indicator.dart';
3 | import 'package:flutter_maimai/components/app_bar_action.dart';
4 | import 'package:flutter_maimai/components/customer_tab_item.dart';
5 | import 'package:flutter_maimai/components/search_field.dart';
6 | import 'package:flutter_maimai/components/self_icon.dart';
7 | import 'package:flutter_maimai/constants.dart';
8 | import 'package:flutter_maimai/pages/home/ask.dart';
9 | import 'package:flutter_maimai/pages/home/follows.dart';
10 | import 'package:flutter_maimai/pages/home/hot.dart';
11 | import 'package:flutter_maimai/pages/home/job.dart';
12 | import 'package:flutter_maimai/pages/home/recommend.dart';
13 | import 'package:flutter_maimai/types.dart';
14 |
15 | class Home extends StatefulWidget {
16 | const Home({Key? key}) : super(key: key);
17 |
18 | @override
19 | _HomeState createState() => _HomeState();
20 | }
21 |
22 | class _HomeState extends State with SingleTickerProviderStateMixin {
23 | late TabController _tabController;
24 | int idx = 0;
25 | final List _tabList = ['关注', '推荐', '热门', '职言', '问员工'];
26 | @override
27 | void initState() {
28 | super.initState();
29 | _tabController = new TabController(vsync: this, length: 5, initialIndex: 1);
30 | }
31 |
32 | void _changeActiveIdx(int index) {
33 | print('修改idx:$index');
34 | setState(() {
35 | idx = index;
36 | });
37 | }
38 |
39 | AppBar _buildAppBarHistory() {
40 | return AppBar(
41 | title: SearchField(
42 | haveGap: false,
43 | placeholder: '找同学:清华大学计算机专业',
44 | ),
45 | centerTitle: false,
46 | actions: [
47 | AppBarAction(
48 | icon: SelfIcon.AddBold,
49 | press: () {},
50 | right: iDefaultPadding / 2,
51 | ),
52 | AppBarAction(
53 | icon: SelfIcon.AddBold,
54 | press: () {},
55 | right: iDefaultPadding * 0.8,
56 | ),
57 | ],
58 | shadowColor: Colors.transparent,
59 | backgroundColor: iAppBarBackgroundColor,
60 | flexibleSpace: Container(
61 | // height: 30,
62 | // color: Colors.red,
63 | ),
64 | bottom: PreferredSize(
65 | preferredSize: const Size.fromHeight(48.0),
66 | child: Container(
67 | height: 40,
68 | // color: Colors.amber,
69 | padding: EdgeInsets.symmetric(horizontal: iDefaultPadding),
70 | child: Row(
71 | children: [
72 | CustomerTabItem(
73 | title: '关注',
74 | active: idx == 0,
75 | press: () {
76 | _changeActiveIdx(0);
77 | },
78 | isScale: true,
79 | haveBorder: true,
80 | ),
81 | SizedBox(width: iDefaultPadding),
82 | CustomerTabItem(
83 | title: '推荐',
84 | active: idx == 1,
85 | press: () {
86 | _changeActiveIdx(1);
87 | },
88 | isScale: true,
89 | haveBorder: true,
90 | ),
91 | SizedBox(width: iDefaultPadding),
92 | CustomerTabItem(
93 | title: '热门',
94 | active: idx == 2,
95 | press: () {
96 | _changeActiveIdx(2);
97 | },
98 | isScale: true,
99 | haveBorder: true,
100 | ),
101 | SizedBox(width: iDefaultPadding),
102 | CustomerTabItem(
103 | title: '职言',
104 | active: idx == 3,
105 | press: () {
106 | _changeActiveIdx(3);
107 | },
108 | isScale: true,
109 | haveBorder: true,
110 | ),
111 | SizedBox(width: iDefaultPadding),
112 | CustomerTabItem(
113 | title: '问员工',
114 | active: idx == 4,
115 | press: () {
116 | _changeActiveIdx(4);
117 | },
118 | isScale: true,
119 | haveBorder: true,
120 | ),
121 | ],
122 | ),
123 | ),
124 | ),
125 | );
126 | }
127 |
128 | AppBar _buildAppBar() {
129 | return AppBar(
130 | title: SearchField(
131 | haveGap: false,
132 | placeholder: '找同学:清华大学计算机专业',
133 | ),
134 | centerTitle: false,
135 | actions: [
136 | AppBarAction(
137 | icon: SelfIcon.AddBold,
138 | press: () {},
139 | right: iDefaultPadding / 2,
140 | ),
141 | AppBarAction(
142 | icon: SelfIcon.AddBold,
143 | press: () {},
144 | right: iDefaultPadding * 0.8,
145 | ),
146 | ],
147 | shadowColor: Colors.transparent,
148 | backgroundColor: iAppBarBackgroundColor,
149 | bottom: PreferredSize(
150 | preferredSize: const Size.fromHeight(48.0),
151 | child: Container(
152 | alignment: Alignment.centerLeft,
153 | height: 40,
154 | child: TabBar(
155 | tabs: _tabList.map((f) {
156 | return Text(f);
157 | }).toList(),
158 | indicator: CustomerTabSizeIndicator(
159 | borderSide: BorderSide(
160 | width: 3,
161 | ),
162 | width: 20,
163 | ),
164 | controller: _tabController,
165 | indicatorColor: Colors.black,
166 | isScrollable: true,
167 | labelColor: Colors.black,
168 | indicatorWeight: 3.0,
169 | labelStyle: TextStyle(fontSize: 20, fontWeight: FontWeight.w700),
170 | unselectedLabelColor: Colors.black38,
171 | unselectedLabelStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
172 | ),
173 | ),
174 | ),
175 | );
176 | }
177 |
178 | Widget _buildBtn() {
179 | return TextButton(
180 | onPressed: () {
181 | print('点击');
182 | Navigator.pushNamed(
183 | context,
184 | '/postDetail',
185 | arguments: PostDetailParam(1),
186 | );
187 | },
188 | child: Text('去详情'),
189 | );
190 | }
191 |
192 | @override
193 | Widget build(BuildContext context) {
194 | return Scaffold(
195 | appBar: _buildAppBar(),
196 | backgroundColor: iAppBackgroundColor,
197 | body: TabBarView(
198 | controller: _tabController,
199 | children: [
200 | Follows(),
201 | Recommend(),
202 | Hot(),
203 | Job(),
204 | Ask(),
205 | ],
206 | ),
207 | );
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "2.11.0"
12 | boolean_selector:
13 | dependency: transitive
14 | description:
15 | name: boolean_selector
16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "2.1.1"
20 | characters:
21 | dependency: transitive
22 | description:
23 | name: characters
24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "1.3.0"
28 | clock:
29 | dependency: transitive
30 | description:
31 | name: clock
32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
33 | url: "https://pub.dev"
34 | source: hosted
35 | version: "1.1.1"
36 | collection:
37 | dependency: transitive
38 | description:
39 | name: collection
40 | sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.17.1"
44 | cupertino_icons:
45 | dependency: "direct main"
46 | description:
47 | name: cupertino_icons
48 | sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
49 | url: "https://pub.dev"
50 | source: hosted
51 | version: "1.0.5"
52 | fake_async:
53 | dependency: transitive
54 | description:
55 | name: fake_async
56 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
57 | url: "https://pub.dev"
58 | source: hosted
59 | version: "1.3.1"
60 | flutter:
61 | dependency: "direct main"
62 | description: flutter
63 | source: sdk
64 | version: "0.0.0"
65 | flutter_svg:
66 | dependency: "direct main"
67 | description:
68 | name: flutter_svg
69 | sha256: "9ac1967e2f72a08af11b05b39167920f90d043cf67163d13a544a358c8f31afa"
70 | url: "https://pub.dev"
71 | source: hosted
72 | version: "0.22.0"
73 | flutter_test:
74 | dependency: "direct dev"
75 | description: flutter
76 | source: sdk
77 | version: "0.0.0"
78 | js:
79 | dependency: transitive
80 | description:
81 | name: js
82 | sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
83 | url: "https://pub.dev"
84 | source: hosted
85 | version: "0.6.7"
86 | matcher:
87 | dependency: transitive
88 | description:
89 | name: matcher
90 | sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
91 | url: "https://pub.dev"
92 | source: hosted
93 | version: "0.12.15"
94 | material_color_utilities:
95 | dependency: transitive
96 | description:
97 | name: material_color_utilities
98 | sha256: "586678f20e112219ed0f73215f01bcdf1d769824ba2ebae45ad918a9bfde9bdb"
99 | url: "https://pub.dev"
100 | source: hosted
101 | version: "0.3.0"
102 | meta:
103 | dependency: transitive
104 | description:
105 | name: meta
106 | sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
107 | url: "https://pub.dev"
108 | source: hosted
109 | version: "1.9.1"
110 | path:
111 | dependency: transitive
112 | description:
113 | name: path
114 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
115 | url: "https://pub.dev"
116 | source: hosted
117 | version: "1.8.3"
118 | path_drawing:
119 | dependency: transitive
120 | description:
121 | name: path_drawing
122 | sha256: "3bdd251dae9ffaef944450b73f168610db7e968e7b20daf0c3907f8b4aafc8a2"
123 | url: "https://pub.dev"
124 | source: hosted
125 | version: "0.5.1+1"
126 | path_parsing:
127 | dependency: transitive
128 | description:
129 | name: path_parsing
130 | sha256: ee5c47c1058ad66b4a41746ec3996af9593d0858872807bcd64ac118f0700337
131 | url: "https://pub.dev"
132 | source: hosted
133 | version: "0.2.1"
134 | petitparser:
135 | dependency: transitive
136 | description:
137 | name: petitparser
138 | sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
139 | url: "https://pub.dev"
140 | source: hosted
141 | version: "5.4.0"
142 | sky_engine:
143 | dependency: transitive
144 | description: flutter
145 | source: sdk
146 | version: "0.0.99"
147 | source_span:
148 | dependency: transitive
149 | description:
150 | name: source_span
151 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
152 | url: "https://pub.dev"
153 | source: hosted
154 | version: "1.10.0"
155 | stack_trace:
156 | dependency: transitive
157 | description:
158 | name: stack_trace
159 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
160 | url: "https://pub.dev"
161 | source: hosted
162 | version: "1.11.0"
163 | stream_channel:
164 | dependency: transitive
165 | description:
166 | name: stream_channel
167 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
168 | url: "https://pub.dev"
169 | source: hosted
170 | version: "2.1.1"
171 | string_scanner:
172 | dependency: transitive
173 | description:
174 | name: string_scanner
175 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
176 | url: "https://pub.dev"
177 | source: hosted
178 | version: "1.2.0"
179 | term_glyph:
180 | dependency: transitive
181 | description:
182 | name: term_glyph
183 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
184 | url: "https://pub.dev"
185 | source: hosted
186 | version: "1.2.1"
187 | test_api:
188 | dependency: transitive
189 | description:
190 | name: test_api
191 | sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
192 | url: "https://pub.dev"
193 | source: hosted
194 | version: "0.5.1"
195 | vector_math:
196 | dependency: transitive
197 | description:
198 | name: vector_math
199 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
200 | url: "https://pub.dev"
201 | source: hosted
202 | version: "2.1.4"
203 | xml:
204 | dependency: transitive
205 | description:
206 | name: xml
207 | sha256: "80d494c09849dc3f899d227a78c30c5b949b985ededf884cb3f3bcd39f4b447a"
208 | url: "https://pub.dev"
209 | source: hosted
210 | version: "5.4.1"
211 | sdks:
212 | dart: ">=3.0.0-0 <4.0.0"
213 | flutter: ">=1.24.0-7.0"
214 |
--------------------------------------------------------------------------------
/lib/mock/home.dart:
--------------------------------------------------------------------------------
1 | List SpeechMockList = [
2 | {
3 | "id": 1,
4 | "userId": 1,
5 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659613/1000",
6 | "name": "张三",
7 | "tag": "活跃作者",
8 | "post": "CEO",
9 | "company": "宇宙科技有限公司",
10 | "isOn": true,
11 | "content": "狂飙很好看啊,是近些年最好的反腐剧!",
12 | "imgList": [
13 | "https://img2.baidu.com/it/u=912677202,3742700272&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=889",
14 | ],
15 | "tagList": ["设计师", "UI"],
16 | "topic": "你觉得狂飙怎么样?",
17 | "link": "https://uiseed.cn",
18 | "share": 1,
19 | "comment": 10,
20 | "praise": 2,
21 | "vip": true,
22 | },
23 | {
24 | "id": 1,
25 | "userId": 1,
26 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659616/1000",
27 | "name": "李四",
28 | "tag": "活跃用户",
29 | "post": "CFO",
30 | "company": "宇宙科技有限公司",
31 | "isOn": false,
32 | "content": "在线接活,抓紧时间,先到先得!!!!",
33 | "imgList": [
34 | "https://img2.baidu.com/it/u=2021072444,2926411073&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=889",
35 | ],
36 | "tagList": ["前端", "Flutter"],
37 | "topic": "你听听你说的是什么?",
38 | "link": "https://uiseed.cn",
39 | "share": 1,
40 | "comment": 10,
41 | "praise": 2,
42 | "vip": false,
43 | },
44 | {
45 | "id": 1,
46 | "userId": 1,
47 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659874/1000",
48 | "name": "王二狗",
49 | "tag": "签约作者",
50 | "post": "首席运营",
51 | "company": "宇宙科技有限公司",
52 | "isOn": false,
53 | "content": "没啥想说的",
54 | "imgList": [
55 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fitem%2F202002%2F13%2F20200213161934_jruxe.thumb.1000_0.jpg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1678445735&t=43c3fb350da630a559dd83747ed9b960",
56 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fitem%2F201703%2F28%2F20170328205510_S2fwv.thumb.1000_0.jpeg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1678445735&t=a1763ff70281fb518a64d2075497ce64",
57 | "https://hbimg.huabanimg.com/c3f5073a6da479f6fe762620740315f05ff03fc710f79c-zUCDC0_fw658",
58 | ],
59 | "tagList": ["攻城狮", "JavaScript"],
60 | "topic": "你有什么理想?",
61 | "link": "https://uiseed.cn",
62 | "share": 1,
63 | "comment": 10,
64 | "praise": 2,
65 | "vip": true,
66 | },
67 | {
68 | "id": 1,
69 | "userId": 1,
70 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659876/1000",
71 | "name": "王五",
72 | "tag": "活跃用户",
73 | "post": "首席运营",
74 | "company": "某恒星科技有限公司",
75 | "isOn": true,
76 | "content": "你看看下边的图片好看吗?",
77 | "imgList": [
78 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fitem%2F202005%2F30%2F20200530120108_sprwb.thumb.1000_0.png&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1678446149&t=294cdfbeb3d30aa597ab270b5ac9e42e",
79 | ],
80 | "tagList": ["设计师", "前端", "UI"],
81 | "topic": "躺平",
82 | "link": "https://uiseed.cn",
83 | "share": 1,
84 | "comment": 10,
85 | "praise": 2,
86 | "vip": false,
87 | },
88 | {
89 | "id": 1,
90 | "userId": 1,
91 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659879/1000",
92 | "name": "赵六",
93 | "tag": "活跃作者",
94 | "post": "运营经理",
95 | "company": "宇宙科技有限公司",
96 | "isOn": false,
97 | "content": "你瞅瞅,你瞅瞅,看我发的都是啥!",
98 | "imgList": [
99 | "https://img0.baidu.com/it/u=1308361786,4289025415&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=889",
100 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fitem%2F202005%2F30%2F20200530115853_kpkft.png&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1678446185&t=af206f917cc4d837ddb2775521d639e2",
101 | "https://img1.baidu.com/it/u=536270855,1477431967&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=889",
102 | "https://hbimg.b0.upaiyun.com/44621cdd545bea93324659f867c54a53229145a78dc49-oYvA66_fw658",
103 | "https://img2.baidu.com/it/u=3585486134,3253940228&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=889",
104 | "https://img2.baidu.com/it/u=2176132136,1039855159&fm=253&fmt=auto&app=120&f=JPEG?w=800&h=1422"
105 | ],
106 | "tagList": ["前端", "UI"],
107 | "topic": "在线摸鱼",
108 | "link": "https://uiseed.cn",
109 | "share": 1,
110 | "comment": 10,
111 | "praise": 2,
112 | "vip": false,
113 | },
114 | ];
115 |
116 | List AskMockList = [
117 | {
118 | "title": "公司福利怎么样?",
119 | "mention": ["公司大佬", "某位大佬", "行业大佬"],
120 | "answer": {
121 | "logo": "https://inews.gtimg.com/newsapp_bt/0/15103660182/1000",
122 | "name": "公司员工",
123 | "content": "公司福利哪肯定是杠杠滴,这还用说嘛!各种节假日福利,只有想不到,没有没福利的节日。根本不用说,福利溜的很!",
124 | },
125 | "count": 10,
126 | "likes": 22,
127 | },
128 | {
129 | "title": "公司福利怎么样?",
130 | "mention": ["公司大佬", "某位大佬", "行业大佬"],
131 | "answer": {
132 | "logo": "https://inews.gtimg.com/newsapp_bt/0/15103659087/1000",
133 | "name": "公司员工",
134 | "content": "公司福利哪肯定是杠杠滴,这还用说嘛!各种节假日福利,只有想不到,没有没福利的节日。根本不用说,福利溜的很!",
135 | },
136 | "count": 10,
137 | "likes": 22,
138 | },
139 | {
140 | "title": "公司福利怎么样?",
141 | "mention": ["公司大佬", "某位大佬", "行业大佬"],
142 | "answer": {
143 | "logo": "https://inews.gtimg.com/newsapp_bt/0/15103659378/1000",
144 | "name": "公司员工",
145 | "content": "公司福利哪肯定是杠杠滴,这还用说嘛!各种节假日福利,只有想不到,没有没福利的节日。根本不用说,福利溜的很!",
146 | },
147 | "count": 10,
148 | "likes": 22,
149 | },
150 | {
151 | "title": "公司福利怎么样?",
152 | "mention": ["公司大佬", "某位大佬", "行业大佬"],
153 | "answer": {
154 | "logo": "https://inews.gtimg.com/newsapp_bt/0/15103659382/1000",
155 | "name": "公司员工",
156 | "content": "公司福利哪肯定是杠杠滴,这还用说嘛!各种节假日福利,只有想不到,没有没福利的节日。根本不用说,福利溜的很!",
157 | },
158 | "count": 10,
159 | "likes": 22,
160 | },
161 | {
162 | "title": "公司福利怎么样?",
163 | "mention": ["公司大佬", "某位大佬", "行业大佬"],
164 | "answer": {
165 | "logo": "https://inews.gtimg.com/newsapp_bt/0/15103659613/1000",
166 | "name": "公司员工",
167 | "content": "公司福利哪肯定是杠杠滴,这还用说嘛!各种节假日福利,只有想不到,没有没福利的节日。根本不用说,福利溜的很!",
168 | },
169 | "count": 10,
170 | "likes": 22,
171 | },
172 | {
173 | "title": "公司福利怎么样?",
174 | "mention": ["公司大佬", "某位大佬", "行业大佬"],
175 | "answer": {
176 | "logo": "https://inews.gtimg.com/newsapp_bt/0/15103659874/1000",
177 | "name": "公司员工",
178 | "content": "公司福利哪肯定是杠杠滴,这还用说嘛!各种节假日福利,只有想不到,没有没福利的节日。根本不用说,福利溜的很!",
179 | },
180 | "count": 10,
181 | "likes": 22,
182 | },
183 | ];
184 |
185 | List FollowsRecommendMockList = [
186 | {
187 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659087/1000",
188 | "name": "张三",
189 | "company": "网络科技有限公司",
190 | "post": "架构师",
191 | "msgCount": 0,
192 | "vip": true,
193 | },
194 | {
195 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659094/1000",
196 | "name": "李四",
197 | "company": "网络科技有限公司",
198 | "post": "架构师",
199 | "msgCount": 10,
200 | "vip": false,
201 | },
202 | {
203 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659378/1000",
204 | "name": "王五",
205 | "company": "网络科技有限公司",
206 | "post": "架构师",
207 | "msgCount": 128,
208 | "vip": true,
209 | },
210 | {
211 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659379/1000",
212 | "name": "赵六",
213 | "company": "网络科技有限公司",
214 | "post": "架构师",
215 | "msgCount": 0,
216 | "vip": false,
217 | },
218 | {
219 | "avatar": "https://inews.gtimg.com/newsapp_bt/0/15103659382/1000",
220 | "name": "李青",
221 | "company": "网络科技有限公司",
222 | "post": "架构师",
223 | "msgCount": 30,
224 | "vip": false,
225 | },
226 | ];
227 |
228 | List HotListMock = [
229 | {
230 | "name": "离开上家公司后,我认识到了什么,今天来好好说说!",
231 | "isNew": false,
232 | },
233 | {
234 | "name": "哪家公司的食堂这么好,快来围观!",
235 | "isNew": true,
236 | },
237 | {
238 | "name": "XDM快来看看这些题,保证你会感谢我!",
239 | "isNew": false,
240 | },
241 | ];
242 |
243 | List HotTopicListMock = [
244 | {
245 | "title": "汽车行业,营销比安全更重要?",
246 | "desc": "剖析近期电动车事件,谈一谈汽车安全问题。从另一个方面看待汽车行业的营销套路。",
247 | },
248 | {
249 | "title": "寻找最美梦之队",
250 | "desc": "人在职场,压力已经能不仅是来自外部的动荡,还有公司的调整,战略改变等。",
251 | },
252 | ];
253 |
254 | List HotChoicenessColumnMock = [
255 | {
256 | "name": "行业内幕",
257 | "picture":
258 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Fback_pic%2F05%2F21%2F85%2F8659e0786b52bd3.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1631949785&t=8528fe1626c7ff49f166dbbbaa62a7c4",
259 | "tag": "HOT",
260 | "desc": "行业内大佬都在看",
261 | "content": "行业内幕曝光,快点来看看有没有你的行业内幕。",
262 | },
263 | {
264 | "name": "技术专区",
265 | "picture":
266 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.51miz.com%2FElement%2F00%2F85%2F74%2F24%2F29be1852_E857424_2cdb7af2.jpg%21%2Fquality%2F90%2Funsharp%2Ftrue%2Fcompress%2Ftrue%2Fformat%2Fjpg&refer=http%3A%2F%2Fimg.51miz.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1631949808&t=f3e66e074a9828a36b8d06008698f95a",
267 | "tag": "",
268 | "desc": "行业内大佬都在看",
269 | "content": "行业内幕曝光,快点来看看有没有你的行业内幕。",
270 | },
271 | {
272 | "name": "行业内幕",
273 | "picture":
274 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Fback_pic%2F05%2F21%2F85%2F8659e0786b52bd3.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1631949785&t=8528fe1626c7ff49f166dbbbaa62a7c4",
275 | "tag": "NEW",
276 | "desc": "行业内大佬都在看",
277 | "content": "行业内幕曝光,快点来看看有没有你的行业内幕。",
278 | },
279 | {
280 | "name": "技术专区",
281 | "picture":
282 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.51miz.com%2FElement%2F00%2F85%2F74%2F24%2F29be1852_E857424_2cdb7af2.jpg%21%2Fquality%2F90%2Funsharp%2Ftrue%2Fcompress%2Ftrue%2Fformat%2Fjpg&refer=http%3A%2F%2Fimg.51miz.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1631949808&t=f3e66e074a9828a36b8d06008698f95a",
283 | "tag": "",
284 | "desc": "行业内大佬都在看",
285 | "content": "行业内幕曝光,快点来看看有没有你的行业内幕。",
286 | },
287 | {
288 | "name": "行业内幕",
289 | "picture":
290 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Fback_pic%2F05%2F21%2F85%2F8659e0786b52bd3.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1631949785&t=8528fe1626c7ff49f166dbbbaa62a7c4",
291 | "tag": "HOT",
292 | "desc": "行业内大佬都在看",
293 | "content": "行业内幕曝光,快点来看看有没有你的行业内幕。",
294 | },
295 | {
296 | "name": "技术专区",
297 | "picture":
298 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.51miz.com%2FElement%2F00%2F85%2F74%2F24%2F29be1852_E857424_2cdb7af2.jpg%21%2Fquality%2F90%2Funsharp%2Ftrue%2Fcompress%2Ftrue%2Fformat%2Fjpg&refer=http%3A%2F%2Fimg.51miz.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1631949808&t=f3e66e074a9828a36b8d06008698f95a",
299 | "tag": "HOT",
300 | "desc": "行业内大佬都在看",
301 | "content": "行业内幕曝光,快点来看看有没有你的行业内幕。",
302 | },
303 | ];
304 |
305 | List HotContentListMock = [];
306 |
--------------------------------------------------------------------------------
/lib/pages/home/components/speech_item.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math' as math;
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_maimai/components/card_container.dart';
4 | import 'package:flutter_maimai/components/self_icon.dart';
5 | import 'package:flutter_maimai/components/tag.dart';
6 | import 'package:flutter_maimai/components/vip_badge.dart';
7 | import 'package:flutter_maimai/constants.dart';
8 | import 'package:flutter_maimai/types.dart';
9 |
10 | class SpeechItem extends StatefulWidget {
11 | const SpeechItem({
12 | Key? key,
13 | required this.speech,
14 | }) : super(key: key);
15 |
16 | final speech;
17 |
18 | @override
19 | _SpeechItemState createState() => _SpeechItemState();
20 | }
21 |
22 | class _SpeechItemState extends State {
23 | String _dealCompanyInfo() {
24 | String _prefix = widget.speech['isOn'] ? '' : '前';
25 | String _company = widget.speech['company'];
26 | String _post = widget.speech['post'];
27 | return '$_prefix$_company$_post';
28 | }
29 |
30 | Widget _renderTag() {
31 | String _tag = widget.speech['tag'];
32 | if (_tag == '') {
33 | return Text('');
34 | } else {
35 | return Tag(_tag);
36 | }
37 | }
38 |
39 | Widget _buildHeader() {
40 | Size size = MediaQuery.of(context).size;
41 | bool _isVip = widget.speech['vip'];
42 | return Row(
43 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
44 | crossAxisAlignment: CrossAxisAlignment.start,
45 | children: [
46 | GestureDetector(
47 | onTap: () {
48 | Navigator.pushNamed(context, '/user', arguments: UserDetailParam(widget.speech['userId']));
49 | },
50 | child: Row(
51 | children: [
52 | CircleAvatar(
53 | radius: 20,
54 | backgroundImage: NetworkImage(widget.speech['avatar']),
55 | ),
56 | SizedBox(width: iDefaultPadding / 2),
57 | Column(
58 | crossAxisAlignment: CrossAxisAlignment.start,
59 | children: [
60 | Row(
61 | children: [
62 | Text(
63 | widget.speech['name'],
64 | style: TextStyle(fontSize: 18, color: Colors.black, fontWeight: FontWeight.w500),
65 | ),
66 | SizedBox(width: iDefaultPadding / 4),
67 | _renderTag(),
68 | ],
69 | ),
70 | Container(
71 | width: size.width * 0.65,
72 | child: Row(
73 | children: [
74 | Text(
75 | _dealCompanyInfo(),
76 | overflow: TextOverflow.ellipsis,
77 | style: TextStyle(fontSize: 14, color: Colors.black38),
78 | ),
79 | SizedBox(width: 3),
80 | _isVip ? VipBadge() : Text(''),
81 | ],
82 | ),
83 | ),
84 | ],
85 | ),
86 | ],
87 | ),
88 | ),
89 | InkWell(
90 | onTap: () {},
91 | child: Transform.rotate(
92 | angle: math.pi / 2,
93 | child: Icon(
94 | SelfIcon.Elipsis,
95 | size: 16,
96 | color: Colors.black26,
97 | ),
98 | ),
99 | ),
100 | ],
101 | );
102 | }
103 |
104 | // 生成单个tag标签
105 | Widget _buildTagItem(String tag) {
106 | return GestureDetector(
107 | onTap: () {
108 | // 跳转到搜索页面,并把标签内容带入
109 | },
110 | child: Padding(
111 | padding: EdgeInsets.only(right: iDefaultPadding / 4, top: iDefaultPadding / 2, bottom: iDefaultPadding / 2),
112 | child: Text(
113 | '#$tag',
114 | style: TextStyle(fontSize: 16, color: iPrimaryColor),
115 | ),
116 | ),
117 | );
118 | }
119 |
120 | // 生成tag列表
121 | Widget _buildTagList() {
122 | List _tagList = [];
123 | List _tagData = widget.speech['tagList'];
124 | for (int i = 0; i < _tagData.length; i++) {
125 | _tagList.add(_buildTagItem(_tagData[i]));
126 | }
127 | return Row(
128 | children: _tagList,
129 | );
130 | }
131 |
132 | Widget _buildImageItem(String image, bool isSingle) {
133 | Size size = MediaQuery.of(context).size;
134 | double _allWidth = size.width - iDefaultPadding * 4;
135 | double _width = isSingle ? _allWidth / 2 : (_allWidth - iDefaultPadding) / 3;
136 | return InkWell(
137 | onTap: () {
138 | // 点击预览
139 | },
140 | child: isSingle
141 | ? Container(
142 | width: _width,
143 | margin: EdgeInsets.only(top: iDefaultPadding / 2),
144 | decoration: BoxDecoration(
145 | borderRadius: iBorderRadius,
146 | border: new Border.all(color: Colors.black54, width: 0.1),
147 | ),
148 | child: ClipRRect(
149 | borderRadius: BorderRadius.circular(10),
150 | child: Image.network(
151 | image,
152 | fit: BoxFit.cover,
153 | ),
154 | ),
155 | )
156 | : Container(
157 | width: _width,
158 | height: _width,
159 | margin: EdgeInsets.only(top: iDefaultPadding / 2),
160 | decoration: BoxDecoration(
161 | borderRadius: iBorderRadius,
162 | border: new Border.all(color: Colors.black54, width: 0.1),
163 | image: new DecorationImage(
164 | image: new NetworkImage(image),
165 | fit: BoxFit.cover,
166 | ),
167 | ),
168 | ),
169 | );
170 | }
171 |
172 | Widget _buildImageList() {
173 | List _imgList = [];
174 | List _imgData = widget.speech['imgList'];
175 | int len = _imgData.length;
176 | Widget _more = Text('');
177 | int _targetLen = len;
178 | if (len == 1) {
179 | return _buildImageItem(_imgData[0], true);
180 | } else if (len > 3) {
181 | _targetLen = 3;
182 | _more = Positioned(
183 | right: iDefaultPadding / 2,
184 | bottom: iDefaultPadding,
185 | child: Container(
186 | height: 20,
187 | padding: EdgeInsets.symmetric(horizontal: iDefaultPadding / 4),
188 | decoration: BoxDecoration(
189 | color: Colors.black54,
190 | borderRadius: BorderRadius.all(Radius.circular(4)),
191 | ),
192 | child: Center(
193 | child: Text('+${len - 3}', style: TextStyle(color: Colors.white, fontSize: 12)),
194 | ),
195 | ),
196 | );
197 | }
198 |
199 | for (int i = 0; i < _targetLen; i++) {
200 | _imgList.add(_buildImageItem(_imgData[i], false));
201 | if (i != _targetLen - 1) {
202 | _imgList.add(SizedBox(width: iDefaultPadding / 2));
203 | }
204 | }
205 | return Stack(
206 | children: [
207 | Row(
208 | children: _imgList,
209 | ),
210 | _more,
211 | ],
212 | );
213 | }
214 |
215 | // 生成主体内容
216 | Widget _buildContent(Size size) {
217 | String _content = widget.speech['content'];
218 | return GestureDetector(
219 | onTap: () {},
220 | child: Container(
221 | padding: EdgeInsets.only(top: iDefaultPadding / 2),
222 | constraints: BoxConstraints(
223 | // maxHeight: 300,
224 | minHeight: 30,
225 | ),
226 | decoration: BoxDecoration(),
227 | child: Column(
228 | crossAxisAlignment: CrossAxisAlignment.start,
229 | children: [
230 | Text(
231 | _content,
232 | overflow: TextOverflow.ellipsis,
233 | maxLines: 4,
234 | style: TextStyle(
235 | fontSize: 18,
236 | color: Colors.black,
237 | ),
238 | ),
239 | _buildImageList(),
240 | _buildTagList(),
241 | ],
242 | ),
243 | ),
244 | );
245 | }
246 |
247 | Widget _buildFooter() {
248 | int _share = widget.speech['share'];
249 | int _comment = widget.speech['comment'];
250 | int _praise = widget.speech['praise'];
251 | return Padding(
252 | padding: EdgeInsets.only(top: iDefaultPadding),
253 | child: Row(
254 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
255 | children: [
256 | BottomItem(
257 | icon: SelfIcon.Share,
258 | value: _share,
259 | press: () {},
260 | ),
261 | BottomItem(
262 | icon: SelfIcon.Comment,
263 | value: _comment,
264 | press: () {},
265 | ),
266 | BottomItem(
267 | icon: SelfIcon.Good,
268 | value: _praise,
269 | press: () {},
270 | ),
271 | ],
272 | ),
273 | );
274 | }
275 |
276 | Widget _buildTopic() {
277 | String _topic = widget.speech['topic'];
278 | if (_topic == '') return Text('');
279 | return Container(
280 | height: 30,
281 | padding: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
282 | decoration: BoxDecoration(
283 | color: iPrimaryColor.withOpacity(0.05),
284 | borderRadius: BorderRadius.all(Radius.circular(30)),
285 | ),
286 | child: Row(
287 | mainAxisSize: MainAxisSize.min,
288 | children: [
289 | Container(
290 | width: 15,
291 | height: 15,
292 | margin: EdgeInsets.only(right: iDefaultPadding / 4),
293 | decoration: BoxDecoration(
294 | color: iPrimaryColor.withOpacity(0.5),
295 | borderRadius: BorderRadius.all(Radius.circular(15)),
296 | ),
297 | child: Center(
298 | child: Text(
299 | '#',
300 | style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold),
301 | ),
302 | ),
303 | ),
304 | Text(
305 | _topic,
306 | style: TextStyle(fontSize: 12, color: iPrimaryColor.withOpacity(0.5), fontWeight: FontWeight.w500),
307 | ),
308 | ],
309 | ),
310 | );
311 | }
312 |
313 | @override
314 | Widget build(BuildContext context) {
315 | Size size = MediaQuery.of(context).size;
316 | return CardContainer(
317 | margin: EdgeInsets.only(left: iDefaultPadding / 2, right: iDefaultPadding / 2, bottom: iDefaultPadding / 2),
318 | padding: EdgeInsets.all(iDefaultPadding),
319 | child: Column(
320 | crossAxisAlignment: CrossAxisAlignment.start,
321 | children: [
322 | _buildHeader(),
323 | _buildContent(size),
324 | _buildTopic(),
325 | _buildFooter(),
326 | ],
327 | ),
328 | );
329 | }
330 | }
331 |
332 | class BottomItem extends StatelessWidget {
333 | const BottomItem({
334 | Key? key,
335 | required this.value,
336 | required this.icon,
337 | required this.press,
338 | }) : super(key: key);
339 |
340 | final int value;
341 | final IconData icon;
342 | final VoidCallback press;
343 |
344 | @override
345 | Widget build(BuildContext context) {
346 | return Expanded(
347 | child: InkWell(
348 | onTap: press,
349 | child: Center(
350 | child: Row(
351 | mainAxisAlignment: MainAxisAlignment.center,
352 | crossAxisAlignment: CrossAxisAlignment.center,
353 | children: [
354 | Icon(
355 | icon,
356 | size: 20,
357 | color: Colors.black54,
358 | ),
359 | SizedBox(width: iDefaultPadding / 2),
360 | Text(
361 | '$value',
362 | style: TextStyle(
363 | fontSize: 16,
364 | fontWeight: FontWeight.w500,
365 | color: Colors.black54,
366 | ),
367 | ),
368 | ],
369 | ),
370 | ),
371 | ),
372 | );
373 | }
374 | }
375 |
--------------------------------------------------------------------------------