├── ios
├── Flutter
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── AppFrameworkInfo.plist
├── 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
├── Runner.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── xcshareddata
│ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ └── project.pbxproj
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
└── .gitignore
├── README.md
├── images
├── vip_dropdown_up.png
├── vip_dropdown_down.png
├── vip_dropdown_normal.png
├── vip_dropdown_selected.png
├── vip_ic_image_default.png
├── vip_ic_rating_normal.webp
├── vip_ic_button_down_load.webp
├── vip_ic_button_progress.webp
├── vip_ic_rating_selected.webp
├── vip_ic_button_send_invite.webp
├── vip_dropdown_single_selected.png
├── vip_ic_button_warn_progress.webp
└── vip_ic_button_select_progress.webp
├── 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
│ │ │ │ │ └── vip
│ │ │ │ │ └── flutter
│ │ │ │ │ └── flutter_widget
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ └── profile
│ │ │ └── AndroidManifest.xml
│ └── build.gradle
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── .gitignore
├── settings.gradle
└── build.gradle
├── lib
├── data
│ └── bean
│ │ ├── main_tab_bean.dart
│ │ ├── view_item_bean.dart
│ │ └── text_rich_bean.dart
├── constants
│ ├── dimen_constant.dart
│ ├── color_constant.dart
│ ├── toast_type.dart
│ ├── image_constant.dart
│ └── string_constant.dart
├── ui
│ ├── page
│ │ ├── net
│ │ │ └── net_page.dart
│ │ ├── list
│ │ │ └── list_page.dart
│ │ ├── home
│ │ │ └── home_page.dart
│ │ ├── view
│ │ │ ├── video
│ │ │ │ └── video_page.dart
│ │ │ ├── light
│ │ │ │ └── TextHighLightPage.dart
│ │ │ ├── bar
│ │ │ │ ├── app_bar_page.dart
│ │ │ │ └── tab_bar_page.dart
│ │ │ ├── view_page.dart
│ │ │ ├── image
│ │ │ │ └── image_page.dart
│ │ │ ├── banner
│ │ │ │ └── banner_page.dart
│ │ │ ├── text
│ │ │ │ └── text_page.dart
│ │ │ └── button
│ │ │ │ └── button_page.dart
│ │ ├── main
│ │ │ └── main_tab.dart
│ │ └── util
│ │ │ ├── drop
│ │ │ └── popup_window_page.dart
│ │ │ ├── util_page.dart
│ │ │ └── toast
│ │ │ └── toast_page.dart
│ └── widget
│ │ ├── text_high_light.dart
│ │ ├── vip_btn_rotate_animate.dart
│ │ ├── vip_button.dart
│ │ ├── dashed_border.dart
│ │ ├── vip_image.dart
│ │ ├── vip_app_bar_view.dart
│ │ ├── vip_tab_bar_view.dart
│ │ ├── vip_text.dart
│ │ ├── vip_video_controller.dart
│ │ └── vip_banner.dart
├── utils
│ ├── hex_color.dart
│ ├── router_util.dart
│ ├── router_manage.dart
│ ├── popup_window.dart
│ ├── time_util.dart
│ └── toast.dart
├── test.dart
├── main.dart
└── base
│ ├── base_page_state.dart
│ └── base_widget.dart
├── .gitignore
├── test
└── widget_test.dart
├── .metadata
├── analysis_options.yaml
├── pubspec.yaml
└── pubspec.lock
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # flutter_widget
2 |
3 | Flutter组件封装,包含了文本、图片、轮播图、网络、列表、tab选项卡等等功能,长期更新,不定期维护!
4 |
--------------------------------------------------------------------------------
/images/vip_dropdown_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/HEAD/images/vip_dropdown_up.png
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/images/vip_dropdown_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/HEAD/images/vip_dropdown_down.png
--------------------------------------------------------------------------------
/images/vip_dropdown_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/HEAD/images/vip_dropdown_normal.png
--------------------------------------------------------------------------------
/images/vip_dropdown_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/HEAD/images/vip_dropdown_selected.png
--------------------------------------------------------------------------------
/images/vip_ic_image_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/HEAD/images/vip_ic_image_default.png
--------------------------------------------------------------------------------
/images/vip_ic_rating_normal.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/HEAD/images/vip_ic_rating_normal.webp
--------------------------------------------------------------------------------
/images/vip_ic_button_down_load.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/HEAD/images/vip_ic_button_down_load.webp
--------------------------------------------------------------------------------
/images/vip_ic_button_progress.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/HEAD/images/vip_ic_button_progress.webp
--------------------------------------------------------------------------------
/images/vip_ic_rating_selected.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/HEAD/images/vip_ic_rating_selected.webp
--------------------------------------------------------------------------------
/images/vip_ic_button_send_invite.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/HEAD/images/vip_ic_button_send_invite.webp
--------------------------------------------------------------------------------
/images/vip_dropdown_single_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/HEAD/images/vip_dropdown_single_selected.png
--------------------------------------------------------------------------------
/images/vip_ic_button_warn_progress.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/HEAD/images/vip_ic_button_warn_progress.webp
--------------------------------------------------------------------------------
/images/vip_ic_button_select_progress.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/HEAD/images/vip_ic_button_select_progress.webp
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/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/AbnerMing888/flutter_widget/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/vip/flutter/flutter_widget/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.vip.flutter.flutter_widget
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
6 |
--------------------------------------------------------------------------------
/lib/data/bean/main_tab_bean.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | ///AUTHOR:AbnerMing
4 | ///DATE:2023/5/11
5 | ///INTRODUCE:主页面底部Tab对象
6 |
7 | class MainTabBean{
8 | String name;
9 | IconData icon;
10 | MainTabBean(this.name,this.icon);
11 |
12 | }
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/data/bean/view_item_bean.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | ///AUTHOR:AbnerMing
4 | ///DATE:2023/5/11
5 | ///INTRODUCE:View页面ItemBean
6 |
7 | class ViewItemBean {
8 | String name;
9 | String path;
10 | IconData icon;
11 |
12 | ViewItemBean(this.name, this.path, this.icon);
13 | }
14 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/constants/dimen_constant.dart:
--------------------------------------------------------------------------------
1 | ///AUTHOR:AbnerMing
2 | ///DATE:2023/5/15
3 | ///INTRODUCE:尺寸常量
4 | class DimenConstant {
5 | static const double dimen_5 = 5;
6 | static const double dimen_10 = 10;
7 | static const double dimen_15 = 15;
8 | static const double dimen_22 = 22;
9 | static const double dimen_44 = 44;
10 | }
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.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 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/lib/data/bean/text_rich_bean.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | ///AUTHOR:AbnerMing
4 | ///DATE:2023/5/16
5 | ///INTRODUCE:文本控件之富文本对象
6 |
7 | class TextRichBean {
8 | String? text; //文字
9 | Color? textColor; //文字颜色
10 | double? textSize; //文字大小
11 | String? link; //文字链接
12 | TextRichBean({this.text, this.textColor, this.textSize, this.link});
13 | }
14 |
--------------------------------------------------------------------------------
/lib/constants/color_constant.dart:
--------------------------------------------------------------------------------
1 | ///AUTHOR:AbnerMing
2 | ///DATE:2023/5/15
3 | ///INTRODUCE:颜色常量
4 |
5 | class ColorConstant {
6 | static const String color_74d1d7 = "#74d1d7";
7 | static const String color_d4d4d4 = "#d4d4d4";
8 | static const String color_f64749 = "#f64749";
9 | static const String color_44b9c0 = "#44b9c0";
10 | static const String color_f7fbfb = "#f7fbfb";
11 | }
--------------------------------------------------------------------------------
/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/constants/toast_type.dart:
--------------------------------------------------------------------------------
1 | ///AUTHOR:AbnerMing
2 | ///DATE:2023/5/12
3 | ///INTRODUCE:Toast 相关类型参数
4 |
5 | enum ToastPosition {
6 | top,
7 | center,
8 | bottom,
9 | }
10 |
11 | /// icon位置
12 | enum ToastIconPosition {
13 | left,
14 | top,
15 | right,
16 | bottom,
17 | }
18 |
19 | ///提示
20 | enum ToastPrompt {
21 | success,
22 | error,
23 | warn,
24 | loading,
25 | none,
26 | }
27 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/lib/ui/page/net/net_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/widgets.dart';
2 |
3 | ///AUTHOR:AbnerMing
4 | ///DATE:2023/5/11
5 | ///INTRODUCE:网络页面
6 |
7 | class NetPage extends StatefulWidget {
8 | const NetPage({super.key});
9 |
10 | @override
11 | State createState() => _NetPageState();
12 | }
13 |
14 | class _NetPageState extends State {
15 | @override
16 | Widget build(BuildContext context) {
17 | return const Text("网络页面");
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/lib/ui/page/list/list_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/widgets.dart';
2 |
3 | ///AUTHOR:AbnerMing
4 | ///DATE:2023/5/11
5 | ///INTRODUCE:列表页面
6 |
7 | class ListPage extends StatefulWidget {
8 | const ListPage({super.key});
9 |
10 | @override
11 | State createState() => _ListPageState();
12 | }
13 |
14 | class _ListPageState extends State {
15 | @override
16 | Widget build(BuildContext context) {
17 | return const Text("列表页面");
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/lib/utils/hex_color.dart:
--------------------------------------------------------------------------------
1 | import 'dart:ui';
2 |
3 | ///AUTHOR:AbnerMing
4 | ///DATE:2023/5/15
5 | ///INTRODUCE:16进制颜色值
6 |
7 | class HexColor extends Color {
8 | static int _getColorFromHex(String hexColor) {
9 | hexColor = hexColor.toUpperCase().replaceAll("#", "");
10 | if (hexColor.length == 6) {
11 | hexColor = "FF$hexColor";
12 | }
13 | return int.parse(hexColor, radix: 16);
14 | }
15 |
16 | HexColor(final String hexColor) : super(_getColorFromHex(hexColor));
17 | }
18 |
--------------------------------------------------------------------------------
/lib/test.dart:
--------------------------------------------------------------------------------
1 | void main() {
2 | String _content = "1239456789";
3 | String _searchContent = "8";
4 | int start = 0;
5 | int end;
6 | while ((end = _content.indexOf(_searchContent, start)) != -1) {
7 | if (end != 0) {
8 | print(_content.substring(start, end) + "===$end");
9 | }
10 | print(_searchContent);
11 | start = end + _searchContent.length;
12 | }
13 | if(start != _content.length){
14 | print(_content.substring(start, _content.length)+"====$start");
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/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/ui/page/home/home_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../base/base_page_state.dart';
4 |
5 | ///AUTHOR:AbnerMing
6 | ///DATE:2023/5/11
7 | ///INTRODUCE:主页面
8 |
9 | class HomePage extends StatefulWidget {
10 | const HomePage({super.key});
11 |
12 | @override
13 | State createState() => _HomePageState();
14 | }
15 |
16 | class _HomePageState extends BasePageState {
17 | @override
18 | bool showAppBar() {
19 | return false;
20 | }
21 |
22 | @override
23 | String getTitle() {
24 | return "主页";
25 | }
26 |
27 | @override
28 | Widget getBody(BuildContext context) {
29 | return const Text("主页");
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.7.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.2.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/lib/constants/image_constant.dart:
--------------------------------------------------------------------------------
1 | ///AUTHOR:AbnerMing
2 | ///DATE:2023/5/15
3 | ///INTRODUCE:图片地址常量
4 | class ImageConstant {
5 | static const String buttonProgress = "images/vip_ic_button_progress.webp";
6 | static const String buttonSelectProgress = "images/vip_ic_button_select_progress.webp";
7 | static const String buttonWarnProgress = "images/vip_ic_button_warn_progress.webp";
8 | static const String buttonLeftDownLoad = "images/vip_ic_button_down_load.webp";
9 | static const String buttonLeftSendInvite = "images/vip_ic_button_send_invite.webp";
10 | static const String imageDefault = "images/vip_ic_image_default.png";
11 | static const String ratingNormal = "images/vip_ic_rating_normal.webp";
12 | static const String ratingSelected = "images/vip_ic_rating_selected.webp";
13 | }
--------------------------------------------------------------------------------
/lib/constants/string_constant.dart:
--------------------------------------------------------------------------------
1 | ///AUTHOR:AbnerMing
2 | ///DATE:2023/5/15
3 | ///INTRODUCE:字符串常量
4 |
5 | class StringConstant {
6 | static const String mainButton = "主按钮 Normal";
7 | static const String mainLoadingButton = "主按钮 loading";
8 | static const String mainDisabledButton = "主按钮 Disabled";
9 | static const String secondaryButton = "次按钮 Normal";
10 | static const String secondaryLoadingButton = "次按钮 loading";
11 | static const String secondaryDisabledButton = "次按钮 Disabled";
12 | static const String warningButton = "警示按钮 Normal";
13 | static const String warningLoadingButton = "警示按钮 loading";
14 | static const String warningDisabledButton = "警示按钮 Disabled";
15 | static const String savePhotoButton = "保存到相册";
16 | static const String sendInviteButton = "发送邀请";
17 | static const String gradientButton = " 自定义按钮 (渐变)";
18 | }
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | **/doc/api/
26 | **/ios/Flutter/.last_build_id
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | /build/
34 |
35 | # Symbolication related
36 | app.*.symbols
37 |
38 | # Obfuscation related
39 | app.*.map.json
40 |
41 | # Android Studio will place build artifacts here
42 | /android/app/debug
43 | /android/app/profile
44 | /android/app/release
45 |
--------------------------------------------------------------------------------
/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/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 |
4 | import 'ui/page/main/main_tab.dart';
5 | import 'utils/router_manage.dart';
6 | import 'utils/router_util.dart';
7 |
8 | ///AUTHOR:AbnerMing
9 | ///DATE:2023/5/11
10 | ///INTRODUCE:主入口
11 |
12 | void main() {
13 | runApp(const MyApp());
14 | // 修改状态栏为白色
15 | SystemChrome.setSystemUIOverlayStyle(
16 | const SystemUiOverlayStyle(statusBarColor: Colors.red));
17 | }
18 |
19 | class MyApp extends StatelessWidget {
20 | const MyApp({super.key});
21 |
22 | @override
23 | Widget build(BuildContext context) {
24 | return MaterialApp(
25 | navigatorKey: RouterUtil.navigatorKey,
26 | initialRoute: '/',
27 | onGenerateRoute: onGenerateRoute,
28 | home: const MainTab(),
29 | theme: ThemeData(primarySwatch: Colors.red),
30 | debugShowCheckedModeBanner: false);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 in the flutter_test package. 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_widget/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(const 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 |
--------------------------------------------------------------------------------
/.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.
5 |
6 | version:
7 | revision: 4b12645012342076800eb701bcdfe18f87da21cf
8 | channel: stable
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf
17 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf
18 | - platform: android
19 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf
20 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf
21 | - platform: ios
22 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf
23 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf
24 |
25 | # User provided section
26 |
27 | # List of Local paths (relative to this file) that should be
28 | # ignored by the migrate tool.
29 | #
30 | # Files that are not part of the templates will be ignored by default.
31 | unmanaged_files:
32 | - 'lib/main.dart'
33 | - 'ios/Runner.xcodeproj/project.pbxproj'
34 |
--------------------------------------------------------------------------------
/lib/ui/page/view/video/video_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_widget/base/base_page_state.dart';
4 | import 'package:flutter_widget/ui/widget/vip_video_controller.dart';
5 |
6 | ///AUTHOR:AbnerMing
7 | ///DATE:2023/6/26
8 | ///INTRODUCE:Video页面
9 |
10 | class VideoPage extends StatefulWidget {
11 | const VideoPage({super.key});
12 |
13 | @override
14 | State createState() => _VideoPageState();
15 | }
16 |
17 | class _VideoPageState extends BasePageState {
18 | @override
19 | Widget getBody(BuildContext context) {
20 | return Center(
21 | child: VipVideoController(
22 | totalTime: 1000 * 60,
23 | backgroundColor: Colors.red,
24 | progressColor: Colors.amber,
25 | thumbColor: Colors.red,
26 | textStyle: const TextStyle(color: Colors.red),
27 | onVideoPlayClick: (isPlay) {
28 | print("当前播放按钮状态$isPlay");
29 | },
30 | onVideoFullScreenClick: (isFullScreen) {
31 | print("当前全屏按钮状态$isFullScreen");
32 | },
33 | onVideoChanged: (position) {
34 | //返回毫秒
35 | print("当前拖拽的进度$position");
36 | }
37 | ),
38 | );
39 | }
40 |
41 | @override
42 | String getTitle() {
43 | return "Video控制器";
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/lib/ui/widget/text_high_light.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class TextHighlight extends StatelessWidget {
4 | final TextStyle _ordinaryStyle; //普通的样式
5 | final TextStyle _highlightStyle; //高亮的样式
6 | final String _content; //文本内容
7 | final String _searchContent; //搜索的内容
8 |
9 | const TextHighlight(this._content, this._searchContent, this._ordinaryStyle,
10 | this._highlightStyle,
11 | {super.key});
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | //搜索内容为空
16 | if (_searchContent == "") {
17 | return Text(
18 | _content,
19 | style: _ordinaryStyle,
20 | );
21 | }
22 | List richList = [];
23 | int start = 0;
24 | int end;
25 |
26 | //遍历,进行多处高亮
27 | while ((end = _content.indexOf(_searchContent, start)) != -1) {
28 | //如果搜索内容在开头位置,直接高亮,此处不执行
29 | if (end != 0) {
30 | richList.add(TextSpan(
31 | text: _content.substring(start, end), style: _ordinaryStyle));
32 | }
33 | //高亮内容
34 | richList.add(TextSpan(text: _searchContent, style: _highlightStyle));
35 | //赋值索引
36 | start = end + _searchContent.length;
37 | }
38 | //搜索内容只有在开头或者中间位置,才执行
39 | if (start != _content.length) {
40 | richList.add(TextSpan(
41 | text: _content.substring(start, _content.length),
42 | style: _ordinaryStyle));
43 | }
44 |
45 | return RichText(
46 | text: TextSpan(children: richList),
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/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/ui/widget/vip_btn_rotate_animate.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 |
3 | import '../../constants/dimen_constant.dart';
4 | import '../../constants/image_constant.dart';
5 |
6 | ///AUTHOR:AbnerMing
7 | ///DATE:2023/5/15
8 | ///INTRODUCE:loading旋转动画组件
9 |
10 | class VipBtnRotateAnimate extends StatefulWidget {
11 | final String? imagePath;
12 |
13 | const VipBtnRotateAnimate({super.key, this.imagePath});
14 |
15 | @override
16 | State createState() => _VipBtnRotateAnimateState();
17 | }
18 |
19 | class _VipBtnRotateAnimateState extends State
20 | with SingleTickerProviderStateMixin {
21 |
22 | _VipBtnRotateAnimateState();
23 |
24 | // 会重复播放的控制器
25 | late final AnimationController _repeatController;
26 |
27 | // 线性动画
28 | late final Animation _animation;
29 |
30 | @override
31 | void initState() {
32 | super.initState();
33 | // 动画持续时间是 3秒,此处的this指 TickerProviderStateMixin
34 | _repeatController = AnimationController(
35 | duration: const Duration(seconds: 3),
36 | vsync: this,
37 | )..repeat(); // 设置动画重复播放
38 |
39 | // 创建一个从0到360弧度的补间动画 v * 2 * π
40 | _animation = Tween(begin: 0, end: 1).animate(_repeatController);
41 | }
42 |
43 | @override
44 | void dispose() {
45 | _repeatController.dispose();
46 | super.dispose();
47 | }
48 |
49 | @override
50 | Widget build(BuildContext context) {
51 | String imageSrc = ImageConstant.buttonProgress;
52 | if (widget.imagePath != null) {
53 | imageSrc = widget.imagePath!;
54 | }
55 | return RotationTransition(
56 | turns: _animation,
57 | child: Image.asset(imageSrc,
58 | width: DimenConstant.dimen_22, height: DimenConstant.dimen_22),
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/lib/base/base_page_state.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | ///AUTHOR:AbnerMing
4 | ///DATE:2023/5/11
5 | ///INTRODUCE:页面状态基类
6 |
7 | abstract class BasePageState extends State {
8 | @override
9 | Widget build(BuildContext context) {
10 | return Scaffold(
11 | appBar: showAppBar()
12 | ? AppBar(
13 | title: Text(
14 | getTitle(),
15 | style: const TextStyle(fontSize: 18),
16 | ),
17 | centerTitle: true, //居中
18 | leading: showLeftBack()
19 | ? IconButton(
20 | icon: const Icon(Icons.arrow_back_ios),
21 | onPressed: () {
22 | //点击事件,退出页面
23 | Navigator.pop(context);
24 | },
25 | )
26 | : null,
27 | )
28 | : null,
29 | body: getBody(context),
30 | bottomNavigationBar: getBottomNavigationBar());
31 | }
32 |
33 | String getTitle();
34 |
35 | Widget getBody(BuildContext context);
36 |
37 | /*
38 | * 是否展示左边的返回键
39 | * */
40 | bool showLeftBack() {
41 | return true;
42 | }
43 |
44 | /*
45 | * 是否展示AppBar
46 | * */
47 | bool showAppBar() {
48 | return true;
49 | }
50 |
51 | /*
52 | * 底部Tab
53 | * */
54 | Widget? getBottomNavigationBar() {
55 | return null;
56 | }
57 |
58 | @override
59 | void initState() {
60 | super.initState();
61 | initData();
62 | }
63 |
64 | @override
65 | void dispose() {
66 | super.dispose();
67 | destroy();
68 | }
69 |
70 | /*
71 | *初始化数据
72 | */
73 | void initData() {}
74 |
75 | /*
76 | *组件销毁
77 | */
78 | void destroy() {}
79 | }
80 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
15 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
30 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | Flutter Widget
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | flutter_widget
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(FLUTTER_BUILD_NUMBER)
25 | LSRequiresIPhoneOS
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | UIViewControllerBasedStatusBarAppearance
45 |
46 | CADisableMinimumFrameDurationOnPhone
47 |
48 | UIApplicationSupportsIndirectInputEvents
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/lib/utils/router_util.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | ///AUTHOR:AbnerMing
4 | ///DATE:2023/5/12
5 | ///INTRODUCE:路由工具类
6 |
7 | class RouterUtil {
8 | static final _navigatorKey = GlobalKey();
9 |
10 | static GlobalKey get navigatorKey => _navigatorKey;
11 |
12 | /*
13 | * 用于跳转到指定路由
14 | * @routeName:为目标路由名
15 | * @arguments:传递的参数
16 | * */
17 | static Future pushNamed(String routeName,
18 | {Object? arguments}) async {
19 | return navigatorKey.currentState!
20 | .pushNamed(routeName, arguments: arguments);
21 | }
22 |
23 | /*
24 | * 用于返回上一个路由,并传递返回结果
25 | * @result:为返回结果
26 | * */
27 | static void pop({T? result}) {
28 | navigatorKey.currentState!.pop(result);
29 | }
30 |
31 | /*
32 | * 用于替换当前路由,并跳转到指定路由
33 | * @routeName:为目标路由名
34 | * @arguments:传递的数据
35 | * */
36 | static Future pushReplacementNamed(String routeName,
37 | {Object? arguments}) async {
38 | return navigatorKey.currentState!
39 | .pushReplacementNamed(routeName, arguments: arguments);
40 | }
41 |
42 | /*
43 | * 用于将当前路由替换为指定路由,并从路由栈中删除原路由及之前的所有路由
44 | * @newRouteName:为目标路由名
45 | * @predicate:为过滤条件,用于决定是否删除路由
46 | * @arguments:传递的数据
47 | * */
48 | static Future pushAndRemoveUntil(
49 | String newRouteName, bool Function(Route route) predicate,
50 | {Object? arguments}) async {
51 | return navigatorKey.currentState!
52 | .pushNamedAndRemoveUntil(newRouteName, predicate, arguments: arguments);
53 | }
54 |
55 | /*
56 | * 用于返回上一个路由,并跳转到指定路由
57 | * @newRouteName:为目标路由名
58 | * @arguments:传递的数据
59 | * */
60 | static Future popAndPushNamed(String newRouteName,
61 | {Object? arguments}) async {
62 | return navigatorKey.currentState!
63 | .popAndPushNamed(newRouteName, arguments: arguments);
64 | }
65 |
66 | /*
67 | *获取传递的参数
68 | * */
69 | static dynamic getArgs(BuildContext context) {
70 | return ModalRoute.of(context)?.settings.arguments;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/lib/ui/page/main/main_tab.dart:
--------------------------------------------------------------------------------
1 | import "package:flutter/material.dart";
2 |
3 | import "../../../base/base_page_state.dart";
4 | import "../../../data/bean/main_tab_bean.dart";
5 | import "../home/home_page.dart";
6 | import "../list/list_page.dart";
7 | import "../net/net_page.dart";
8 | import "../util/util_page.dart";
9 | import "../view/view_page.dart";
10 |
11 | ///AUTHOR:AbnerMing
12 | ///DATE:2023/5/11
13 | ///INTRODUCE:主页面底部Tab
14 |
15 | class MainTab extends StatefulWidget {
16 | const MainTab({super.key});
17 |
18 | @override
19 | State createState() => _MainTabState();
20 | }
21 |
22 | class _MainTabState extends BasePageState {
23 | // 当前选中标签的下标
24 | int _currentIndex = 0;
25 |
26 | // 当前页面数组
27 | final List _pageList = [
28 | const HomePage(),
29 | const NetPage(),
30 | const ViewPage(),
31 | const ListPage(),
32 | const UtilPage()
33 | ];
34 |
35 | //当前底部文字列表
36 | final List _pageContent = [
37 | MainTabBean("首页", Icons.home_outlined),
38 | MainTabBean("网络", Icons.webhook),
39 | MainTabBean("View", Icons.grid_view_outlined),
40 | MainTabBean("列表", Icons.list_alt),
41 | MainTabBean("工具", Icons.settings)
42 | ];
43 |
44 | @override
45 | Widget getBody(BuildContext context) {
46 | return _pageList[_currentIndex];
47 | }
48 |
49 | @override
50 | String getTitle() {
51 | return _pageContent[_currentIndex].name;
52 | }
53 |
54 | @override
55 | bool showLeftBack() {
56 | return false;
57 | }
58 |
59 | @override
60 | Widget? getBottomNavigationBar() {
61 | return BottomNavigationBar(
62 | // 当前菜单下标
63 | currentIndex: _currentIndex,
64 | // 点击事件,获取当前点击的标签下标
65 | onTap: (int index) {
66 | setState(() {
67 | _currentIndex = index;
68 | });
69 | },
70 | // 图标大小
71 | iconSize: 30.0,
72 | // 选中图标的颜色
73 | fixedColor: Colors.red,
74 | // 配置底部可以有多个Tab标签页(5个时就需要)
75 | type: BottomNavigationBarType.fixed,
76 | items: _pageContent.map((data) {
77 | return BottomNavigationBarItem(
78 | icon: Icon(data.icon), label: data.name);
79 | }).toList());
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/lib/ui/page/util/drop/popup_window_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_widget/base/base_page_state.dart';
3 | import 'package:flutter_widget/base/base_widget.dart';
4 | import 'package:flutter_widget/ui/widget/vip_text.dart';
5 | import 'package:flutter_widget/utils/popup_window.dart';
6 |
7 | class PopupWindowPage extends StatefulWidget {
8 | const PopupWindowPage({super.key});
9 |
10 | @override
11 | State createState() => DropDownMenuPageState();
12 | }
13 |
14 | class DropDownMenuPageState extends BasePageState {
15 | final GlobalKey _key = GlobalKey();
16 | int _number = 0;
17 |
18 | @override
19 | Widget getBody(BuildContext context) {
20 | return Center(
21 | child: VipText(
22 | "弹出菜单",
23 | key: _key,
24 | onClick: () {
25 | if (_number > 7) {
26 | _number = 0;
27 | }
28 | var direction = WindowDirection.left;
29 | switch (_number) {
30 | case 1:
31 | direction = WindowDirection.top;
32 | break;
33 | case 2:
34 | direction = WindowDirection.right;
35 | break;
36 | case 3:
37 | direction = WindowDirection.bottom;
38 | break;
39 | case 4:
40 | direction = WindowDirection.topLeft;
41 | break;
42 | case 5:
43 | direction = WindowDirection.topRight;
44 | break;
45 | case 6:
46 | direction = WindowDirection.bottomRight;
47 | break;
48 | case 7:
49 | direction = WindowDirection.bottomLeft;
50 | break;
51 | }
52 | PopupWindow.create(
53 | _key,
54 | const BaseWidget(
55 | width: 100,
56 | height: 100,
57 | backgroundColor: Colors.black,
58 | ),
59 | direction: direction,
60 | margin: 10,
61 | childWidth: 100,
62 | childHeight: 100);
63 |
64 | _number++;
65 | },
66 | ),
67 | );
68 | }
69 |
70 | @override
71 | String getTitle() {
72 | return "弹出框";
73 | }
74 |
75 | @override
76 | void destroy() {
77 | PopupWindow.hint();
78 | super.destroy();
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/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 flutter.compileSdkVersion
30 | ndkVersion flutter.ndkVersion
31 |
32 | compileOptions {
33 | sourceCompatibility JavaVersion.VERSION_1_8
34 | targetCompatibility JavaVersion.VERSION_1_8
35 | }
36 |
37 | kotlinOptions {
38 | jvmTarget = '1.8'
39 | }
40 |
41 | sourceSets {
42 | main.java.srcDirs += 'src/main/kotlin'
43 | }
44 |
45 | defaultConfig {
46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
47 | applicationId "com.vip.flutter.flutter_widget"
48 | // You can update the following values to match your application needs.
49 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
50 | minSdkVersion flutter.minSdkVersion
51 | targetSdkVersion flutter.targetSdkVersion
52 | versionCode flutterVersionCode.toInteger()
53 | versionName flutterVersionName
54 | }
55 |
56 | buildTypes {
57 | release {
58 | // TODO: Add your own signing config for the release build.
59 | // Signing with the debug keys for now, so `flutter run --release` works.
60 | signingConfig signingConfigs.debug
61 | }
62 | }
63 | }
64 |
65 | flutter {
66 | source '../..'
67 | }
68 |
69 | dependencies {
70 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
71 | }
72 |
--------------------------------------------------------------------------------
/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/ui/page/util/util_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../constants/dimen_constant.dart';
4 | import '../../../data/bean/view_item_bean.dart';
5 | import '../../../utils/router_manage.dart';
6 | import '../../../utils/router_util.dart';
7 | import '../../widget/vip_text.dart';
8 |
9 | ///AUTHOR:AbnerMing
10 | ///DATE:2023/5/11
11 | ///INTRODUCE:工具类页面
12 |
13 | class UtilPage extends StatefulWidget {
14 | const UtilPage({super.key});
15 |
16 | @override
17 | State createState() => _UtilPageState();
18 | }
19 |
20 | class _UtilPageState extends State {
21 | final List listData = [
22 | ViewItemBean("Toast", RouterManage.toastPage, Icons.whatshot),
23 | ViewItemBean(
24 | "PopupWindowPage", RouterManage.popupWindowPage, Icons.power_input_outlined),
25 | ];
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | return GridView.builder(
30 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
31 | // 定义列
32 | crossAxisCount: 3),
33 | itemCount: listData.length,
34 | itemBuilder: (BuildContext context, int index) => _itemView(index));
35 | }
36 |
37 | /*
38 | * item布局
39 | * @index 当前条目的索引
40 | * */
41 | Widget _itemView(index) {
42 | return Container(
43 | // 子元素
44 | decoration: BoxDecoration(
45 | //设置边框,右边和下边
46 | border: Border(
47 | top: BorderSide.none,
48 | bottom:
49 | BorderSide(width: 1, color: Color.fromRGBO(233, 233, 233, 0.8)),
50 | left: BorderSide.none,
51 | right:
52 | BorderSide(width: 1, color: Color.fromRGBO(233, 233, 233, 0.8)),
53 | )),
54 | alignment: Alignment.center,
55 | child: GestureDetector(
56 | onTap: () {
57 | //view item的点击事件
58 | _itemClick(index);
59 | },
60 | child: Column(
61 | mainAxisAlignment: MainAxisAlignment.center,
62 | children: [
63 | Icon(listData[index].icon, color: Colors.red),
64 | VipText(listData[index].name,
65 | marginTop: DimenConstant.dimen_5,
66 | style: const TextStyle(fontWeight: FontWeight.bold))
67 | ],
68 | ),
69 | ));
70 | }
71 |
72 | /*
73 | * item点击事件
74 | * @param index 当前条目的索引
75 | * */
76 | void _itemClick(index) {
77 | RouterUtil.pushNamed(listData[index].path);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/lib/ui/page/view/light/TextHighLightPage.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_widget/base/base_page_state.dart';
3 | import 'package:flutter_widget/ui/widget/text_high_light.dart';
4 |
5 | ///AUTHOR:AbnerMing
6 | ///DATE:2023/8/14
7 | ///INTRODUCE:组件高亮
8 |
9 | class TextHighLightPage extends StatefulWidget {
10 | const TextHighLightPage({super.key});
11 |
12 | @override
13 | State createState() => TextHighLightPageState();
14 | }
15 |
16 | class TextHighLightPageState extends BasePageState {
17 | final TextEditingController _textControllerContent = TextEditingController();
18 |
19 | final String _content = "床前明月光,疑是地上霜,举头望明月,低头思故乡。";
20 |
21 | String searchContent = ""; //搜索内容
22 |
23 | @override
24 | Widget getBody(BuildContext context) {
25 | return Container(
26 | margin: const EdgeInsets.only(left: 20, top: 20, right: 20),
27 | child: Column(
28 | children: [
29 | SizedBox(
30 | height: 40,
31 | child: TextField(
32 | controller: _textControllerContent,
33 | style: const TextStyle(fontSize: 13),
34 | onChanged: (e) {
35 | //搜索内容
36 | setState(() {
37 | searchContent = e;
38 | });
39 | },
40 | decoration: InputDecoration(
41 | hintText: "搜索内容",
42 | border: OutlineInputBorder(),
43 | contentPadding:
44 | const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
45 | focusedBorder: const OutlineInputBorder(
46 | borderSide: BorderSide(color: Colors.red),
47 | ),
48 | ),
49 | ),
50 | ),
51 | Container(
52 | height: 300,
53 | margin: const EdgeInsets.only(top: 20),
54 | child: ListView.builder(
55 | itemCount: 6,
56 | itemBuilder: (context, index) {
57 | return Container(
58 | margin: const EdgeInsets.only(top: 10),
59 | child: TextHighlight(
60 | _content,
61 | searchContent,
62 | const TextStyle(color: Colors.black),
63 | const TextStyle(color: Colors.red),
64 | ),
65 | );
66 | }))
67 | ],
68 | ),
69 | );
70 | }
71 |
72 | @override
73 | String getTitle() {
74 | return "内容高亮";
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/lib/ui/widget/vip_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../base/base_widget.dart';
4 | import '../../constants/dimen_constant.dart';
5 | import 'vip_btn_rotate_animate.dart';
6 | import 'vip_text.dart';
7 |
8 | ///AUTHOR:AbnerMing
9 | ///DATE:2023/5/11
10 | ///INTRODUCE:Button Widget
11 |
12 | class VipButton extends BaseWidget {
13 | final String text; //文字
14 | final TextStyle? style; //文字样式
15 | final bool? isLeftLoading; //是否开启左边的loading
16 | final String? leftLoadingImage; //左边的loading图片
17 | final String? leftIcon; //左边的图片
18 | final double? leftIconWidth; //左边的图片宽度
19 | final double? leftIconHeight; //左边的图片高度
20 | final double? iconMarginRight; //图片距离右边的距离
21 | const VipButton(this.text,
22 | {super.key,
23 | this.style,
24 | this.isLeftLoading,
25 | this.leftLoadingImage,
26 | this.leftIcon,
27 | this.leftIconWidth,
28 | this.leftIconHeight,
29 | this.iconMarginRight,
30 | super.width,
31 | super.height,
32 | super.margin,
33 | super.marginLeft,
34 | super.marginTop,
35 | super.marginRight,
36 | super.marginBottom,
37 | super.padding,
38 | super.paddingLeft,
39 | super.paddingTop,
40 | super.paddingRight,
41 | super.paddingBottom,
42 | super.backgroundColor,
43 | super.strokeWidth,
44 | super.strokeColor,
45 | super.solidColor,
46 | super.radius,
47 | super.isCircle,
48 | super.leftTopRadius,
49 | super.rightTopRadius,
50 | super.leftBottomRadius,
51 | super.rightBottomRadius,
52 | super.childWidget,
53 | super.alignment,
54 | super.gradient,
55 | super.gradientBegin,
56 | super.gradientEnd,
57 | super.gradientColorList,
58 | super.gradientColorStops,
59 | super.onClick,
60 | super.onDoubleClick,
61 | super.onLongPress});
62 |
63 | @override
64 | Widget getWidget(BuildContext context) {
65 | List widgets = [];
66 | if (isLeftLoading == true) {
67 | //需要加载左边的loading
68 | widgets.add(VipBtnRotateAnimate(imagePath: leftLoadingImage));
69 | }
70 | //左边的Icon
71 | if (leftIcon != null) {
72 | widgets.add(Image.asset(leftIcon!,
73 | width: leftIconWidth ?? DimenConstant.dimen_22,
74 | height: leftIconHeight ?? DimenConstant.dimen_22));
75 | }
76 | widgets.add(VipText(
77 | text,
78 | marginLeft: iconMarginRight ?? DimenConstant.dimen_10,
79 | style: style,
80 | ));
81 | return Row(
82 | mainAxisAlignment: MainAxisAlignment.center,
83 | children: widgets,
84 | );
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/lib/ui/page/view/bar/app_bar_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import '../../../../base/base_page_state.dart';
5 | import '../../../../constants/image_constant.dart';
6 | import '../../../../utils/toast.dart';
7 | import '../../../widget/vip_app_bar_view.dart';
8 |
9 | ///AUTHOR:AbnerMing
10 | ///DATE:2023/5/17
11 | ///INTRODUCE:AppBar组件效果页面
12 |
13 | class AppBarPage extends StatefulWidget {
14 | const AppBarPage({super.key});
15 |
16 | @override
17 | State createState() => _AppBarPageState();
18 | }
19 |
20 | class _AppBarPageState extends BasePageState {
21 | @override
22 | Widget getBody(BuildContext context) {
23 | return Column(children: [
24 | Container(
25 | margin: const EdgeInsets.only(top: 10),
26 | child: VipAppBarView(
27 | "测试标题",
28 | onLeftClick: () {
29 | Toast.toast(context, msg: "左边点击");
30 | },
31 | )),
32 | Container(
33 | margin: const EdgeInsets.only(top: 10),
34 | child: VipAppBarView(
35 | "测试标题",
36 | barIsClose: true,
37 | onLeftClick: () {
38 | Toast.toast(context, msg: "左边点击");
39 | },
40 | onLeftCloseClick: () {
41 | Toast.toast(context, msg: "左边关闭点击");
42 | },
43 | )),
44 | Container(
45 | margin: const EdgeInsets.only(top: 10),
46 | child: VipAppBarView(
47 | "测试标题",
48 | barRightMenuText: "右边",
49 | onLeftClick: () {
50 | Toast.toast(context, msg: "左边点击");
51 | },
52 | onRightMenuTextClick: () {
53 | Toast.toast(context, msg: "文字右边");
54 | },
55 | )),
56 | Container(
57 | margin: const EdgeInsets.only(top: 10),
58 | child: VipAppBarView(
59 | "测试标题",
60 | barRightMenuImage: ImageConstant.buttonLeftSendInvite,
61 | onLeftClick: () {
62 | Toast.toast(context, msg: "左边点击");
63 | },
64 | onRightMenuImageClick: () {
65 | Toast.toast(context, msg: "图片右边");
66 | },
67 | )),
68 | Container(
69 | margin: const EdgeInsets.only(top: 10),
70 | child: VipAppBarView(
71 | "测试标题",
72 | barRightMenuText: "右边",
73 | barRightMenuImage: ImageConstant.buttonLeftSendInvite,
74 | onLeftClick: () {
75 | Toast.toast(context, msg: "左边点击");
76 | },
77 | onRightMenuTextClick: () {
78 | Toast.toast(context, msg: "文字右边");
79 | },
80 | onRightMenuImageClick: () {
81 | Toast.toast(context, msg: "图片右边");
82 | },
83 | )),
84 | ]);
85 | }
86 |
87 | @override
88 | String getTitle() {
89 | return "AppBar";
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/lib/ui/page/view/view_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../base/base_page_state.dart';
4 | import '../../../data/bean/view_item_bean.dart';
5 | import '../../../utils/router_manage.dart';
6 | import '../../../utils/router_util.dart';
7 | import '../../widget/vip_text.dart';
8 |
9 | ///AUTHOR:AbnerMing
10 | ///DATE:2023/5/11
11 | ///INTRODUCE:各个组件页面
12 |
13 | class ViewPage extends StatefulWidget {
14 | const ViewPage({super.key});
15 |
16 | @override
17 | State createState() => _ViewPageState();
18 | }
19 |
20 | class _ViewPageState extends BasePageState {
21 | final List listData = [
22 | ViewItemBean("VipText", RouterManage.textPage, Icons.translate),
23 | ViewItemBean("VipImage", RouterManage.imagePage, Icons.image),
24 | ViewItemBean("VipButton", RouterManage.buttonPage, Icons.touch_app),
25 | ViewItemBean("VipAppbar", RouterManage.appBarPage, Icons.credit_card),
26 | ViewItemBean("VipTab", RouterManage.tabBarPage, Icons.view_module),
27 | ViewItemBean("VipBanner", RouterManage.bannerPage, Icons.view_carousel),
28 | ViewItemBean(
29 | "VideoController", RouterManage.videoPage, Icons.video_collection),
30 | ViewItemBean("TextHighLight", RouterManage.textHighLightPage, Icons.light),
31 | ];
32 |
33 | /*
34 | * item布局
35 | * @index 当前条目的索引
36 | * */
37 | Widget _itemView(index) {
38 | return Container(
39 | // 子元素
40 | decoration: const BoxDecoration(
41 | //设置边框,右边和下边
42 | border: Border(
43 | top: BorderSide.none,
44 | bottom:
45 | BorderSide(width: 1, color: Color.fromRGBO(233, 233, 233, 0.8)),
46 | left: BorderSide.none,
47 | right:
48 | BorderSide(width: 1, color: Color.fromRGBO(233, 233, 233, 0.8)),
49 | )),
50 | alignment: Alignment.center,
51 | child: GestureDetector(
52 | onTap: () {
53 | //view item的点击事件
54 | _itemClick(index);
55 | },
56 | child: Column(
57 | mainAxisAlignment: MainAxisAlignment.center,
58 | children: [
59 | Icon(listData[index].icon, color: Colors.red),
60 | VipText(listData[index].name,
61 | style: const TextStyle(fontWeight: FontWeight.bold))
62 | ],
63 | ),
64 | ));
65 | }
66 |
67 | /*
68 | * item点击事件
69 | * @param index 当前条目的索引
70 | * */
71 | void _itemClick(index) {
72 | RouterUtil.pushNamed(listData[index].path);
73 | }
74 |
75 | @override
76 | String getTitle() {
77 | return "";
78 | }
79 |
80 | @override
81 | bool showAppBar() {
82 | return false;
83 | }
84 |
85 | @override
86 | Widget getBody(BuildContext context) {
87 | return GridView.builder(
88 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
89 | // 定义列
90 | crossAxisCount: 3),
91 | itemCount: listData.length,
92 | itemBuilder: (BuildContext context, int index) => _itemView(index));
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/lib/utils/router_manage.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_widget/ui/page/util/toast/toast_page.dart';
3 | import 'package:flutter_widget/ui/page/view/light/TextHighLightPage.dart';
4 | import 'package:flutter_widget/ui/page/view/video/video_page.dart';
5 |
6 | import '../ui/page/util/drop/popup_window_page.dart';
7 | import '../ui/page/view/banner/banner_page.dart';
8 | import '../ui/page/view/bar/app_bar_page.dart';
9 | import '../ui/page/view/bar/tab_bar_page.dart';
10 | import '../ui/page/view/button/button_page.dart';
11 | import '../ui/page/view/image/image_page.dart';
12 | import '../ui/page/view/text/text_page.dart';
13 |
14 | ///AUTHOR:AbnerMing
15 | ///DATE:2023/5/12
16 | ///INTRODUCE:路由管理
17 |
18 | class RouterManage {
19 | //Button页面路由
20 | static const String buttonPage = "button_page";
21 |
22 | //Text页面路由
23 | static const String textPage = "text_page";
24 |
25 | //Image页面路由
26 | static const String imagePage = "image_page";
27 |
28 | //AppBar页面路由
29 | static const String appBarPage = "app_bar_page";
30 |
31 | //TabBar页面路由
32 | static const String tabBarPage = "tab_bar_page";
33 |
34 | //Banner页面路由
35 | static const String bannerPage = "banner_page";
36 |
37 | //Video页面
38 | static const String videoPage = "video_page";
39 |
40 | //内容高亮页面
41 | static const String textHighLightPage = "text_high_light_page";
42 |
43 | //Toast页面路由
44 | static const String toastPage = "toast_page";
45 |
46 | //popupWindowPage页面路由
47 | static const String popupWindowPage = "popup_window_page";
48 |
49 | //路由地址
50 | final routes = {
51 | //Text页面
52 | textPage: (context) => const TextPage(),
53 | //Image页面
54 | imagePage: (context) => const ImagePage(),
55 | //Button页面
56 | buttonPage: (context) => const ButtonPage(),
57 | //AppBar页面
58 | appBarPage: (context) => const AppBarPage(),
59 | //TabBar页面
60 | tabBarPage: (context) => const TabBarPage(),
61 | //Banner页面
62 | bannerPage: (context) => const BannerPage(),
63 | //Video页面
64 | videoPage: (context) => const VideoPage(),
65 | //内容高亮
66 | textHighLightPage: (context) => const TextHighLightPage(),
67 |
68 | //Toast工具类页面
69 | toastPage: (context) => const ToastPage(),
70 | //下拉菜单
71 | popupWindowPage: (context) => const PopupWindowPage(),
72 | };
73 |
74 | static final RouterManage _instance = RouterManage._();
75 |
76 | RouterManage._();
77 |
78 | factory RouterManage() => _instance;
79 | }
80 |
81 | /*
82 | * 路由模块化
83 | * */
84 | var onGenerateRoute = (RouteSettings settings) {
85 | // 统一处理
86 | final String? name = settings.name;
87 | final Function pageContentBuilder = RouterManage._instance.routes[name]!;
88 | if (settings.arguments != null) {
89 | final Route route = MaterialPageRoute(
90 | builder: (context) =>
91 | pageContentBuilder(context, arguments: settings.arguments));
92 | return route;
93 | } else {
94 | final Route route =
95 | MaterialPageRoute(builder: (context) => pageContentBuilder(context));
96 | return route;
97 | }
98 | };
99 |
--------------------------------------------------------------------------------
/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/ui/widget/dashed_border.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | import '../../constants/color_constant.dart';
6 | import '../../utils/hex_color.dart';
7 |
8 | ///AUTHOR:AbnerMing
9 | ///DATE:2023/5/15
10 | ///INTRODUCE:自定义虚线边框
11 |
12 | class DashedBorder extends CustomPainter {
13 | @override
14 | void paint(Canvas canvas, Size size) {
15 | var paint = Paint()
16 | ..isAntiAlias = true //抗锯齿
17 | ..strokeWidth = 1
18 | ..style = PaintingStyle.stroke //fill填充,stroke线框
19 | ..color = HexColor(ColorConstant.color_74d1d7); //颜色
20 |
21 | var rect = Rect.fromLTWH(0, 0, size.width, size.height);
22 |
23 | canvas.drawDashLine(rect.topLeft, rect.topRight, 4, 4, paint);
24 | canvas.drawDashLine(rect.topLeft, rect.bottomLeft, 4, 4, paint);
25 | canvas.drawDashLine(rect.topRight, rect.bottomRight, 4, 4, paint);
26 | canvas.drawDashLine(rect.bottomLeft, rect.bottomRight, 4, 4, paint);
27 | }
28 |
29 | @override
30 | bool shouldRepaint(covariant CustomPainter oldDelegate) {
31 | // 画布是否需要重绘,false的话,则第一次绘制结束后不可再改变
32 | return false;
33 | }
34 |
35 | }
36 |
37 | ///
38 | /// Canvas扩展函数
39 | /// @Author weitianpeng
40 | /// @Create 2021-3-28
41 | ///
42 | extension CanvasExt on Canvas {
43 | ///绘制虚线
44 | ///[p1] 起点
45 | ///[p2] 终点
46 | ///[dashWidth] 实线宽度
47 | ///[spaceWidth] 空隙宽度
48 | void drawDashLine(
49 | Offset p1,
50 | Offset p2,
51 | double dashWidth,
52 | double spaceWidth,
53 | Paint paint,
54 | ) {
55 | assert(dashWidth > 0);
56 | assert(spaceWidth > 0);
57 |
58 | double radians;
59 |
60 | if (p1.dx == p2.dx) {
61 | radians = (p1.dy < p2.dy) ? pi / 2 : pi / -2;
62 | } else {
63 | radians = atan2(p2.dy - p1.dy, p2.dx - p1.dx);
64 | }
65 |
66 | save();
67 | translate(p1.dx, p1.dy);
68 | rotate(radians);
69 |
70 | var matrix = Matrix4.identity();
71 | matrix.translate(p1.dx, p1.dy);
72 | matrix.rotateZ(radians);
73 | matrix.invert();
74 |
75 | var endPoint = MatrixUtils.transformPoint(matrix, p2);
76 |
77 | double tmp = 0;
78 | double length = endPoint.dx;
79 | double delta;
80 |
81 | while (tmp < length) {
82 | delta = (tmp + dashWidth < length) ? dashWidth : length - tmp;
83 | drawLine(Offset(tmp, 0), Offset(tmp + delta, 0), paint);
84 | if (tmp + delta >= length) {
85 | break;
86 | }
87 |
88 | tmp = (tmp + dashWidth + spaceWidth < length)
89 | ? (tmp + dashWidth + spaceWidth)
90 | : (length);
91 | }
92 |
93 | restore();
94 | }
95 |
96 | ///绘制虚线
97 | ///[rect] 矩形
98 | ///[dashWidth] 实线宽度
99 | ///[spaceWidth] 空隙宽度
100 | void drawDashRect(
101 | Rect rect,
102 | double dashWidth,
103 | double spaceWidth,
104 | Paint paint,
105 | ) {
106 | drawDashLine(rect.topLeft, rect.topRight, dashWidth, spaceWidth, paint);
107 | drawDashLine(rect.topRight, rect.bottomRight, dashWidth, spaceWidth, paint);
108 | drawDashLine(
109 | rect.bottomRight, rect.bottomLeft, dashWidth, spaceWidth, paint);
110 | drawDashLine(rect.bottomLeft, rect.topLeft, dashWidth, spaceWidth, paint);
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/lib/ui/page/view/image/image_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../../base/base_page_state.dart';
4 | import '../../../../constants/dimen_constant.dart';
5 | import '../../../../constants/image_constant.dart';
6 | import '../../../widget/vip_image.dart';
7 | import '../../../widget/vip_text.dart';
8 |
9 | ///AUTHOR:AbnerMing
10 | ///DATE:2023/5/16
11 | ///INTRODUCE:Image组件效果页面
12 | class ImagePage extends StatefulWidget {
13 | const ImagePage({super.key});
14 |
15 | @override
16 | State createState() => _ImagePageState();
17 | }
18 |
19 | class _ImagePageState extends BasePageState {
20 | @override
21 | Widget getBody(BuildContext context) {
22 | return Container(
23 | alignment: Alignment.center,
24 | padding: const EdgeInsets.only(
25 | left: DimenConstant.dimen_10, right: DimenConstant.dimen_10),
26 | child: SingleChildScrollView(
27 | child: Column(
28 | crossAxisAlignment: CrossAxisAlignment.center,
29 | children: const [
30 | VipImage(
31 | "https://www.vipandroid.cn/ming/image/gan.png",
32 | marginTop: DimenConstant.dimen_10,
33 | ),
34 | VipText("(普通加载)", marginTop: DimenConstant.dimen_5),
35 | VipImage(
36 | "https://www.vipandroid.cn/ming/image/gan.png",
37 | marginTop: DimenConstant.dimen_10,
38 | width: 80,
39 | height: 80,
40 | ),
41 | VipText("(设置宽高)", marginTop: DimenConstant.dimen_5),
42 | VipImage(
43 | "https://www.vipandroid.cn/ming/image/gan.png",
44 | marginTop: DimenConstant.dimen_10,
45 | imageBoxFit: BoxFit.fill,
46 | width: 80,
47 | height: 80,
48 | ),
49 | VipText("(设置宽高拉伸充满)", marginTop: DimenConstant.dimen_5),
50 | VipImage(
51 | "https://www.vipandroid.cn/ming/image/gan.png",
52 | marginTop: DimenConstant.dimen_10,
53 | imageBoxFit: BoxFit.cover,
54 | width: 80,
55 | height: 80,
56 | ),
57 | VipText("(设置宽高居中裁切)", marginTop: DimenConstant.dimen_5),
58 | VipImage(
59 | "https://www.vipandroid.cn/ming/image/gan.png",
60 | marginTop: DimenConstant.dimen_10,
61 | imageBoxFit: BoxFit.cover,
62 | placeholderImage: ImageConstant.imageDefault,
63 | width: 80,
64 | height: 80,
65 | ),
66 | VipText("(设置占位图)", marginTop: DimenConstant.dimen_5),
67 | VipImage(
68 | "https://www.vipandroid.cn/ming/image/gan.png",
69 | marginTop: DimenConstant.dimen_10,
70 | imageBoxFit: BoxFit.cover,
71 | isClipOval: true,
72 | width: 80,
73 | height: 80,
74 | ),
75 | VipText("(设置圆形)", marginTop: DimenConstant.dimen_5),
76 | VipImage(
77 | "https://www.vipandroid.cn/ming/image/gan.png",
78 | marginTop: DimenConstant.dimen_10,
79 | imageBoxFit: BoxFit.cover,
80 | imageRadius: DimenConstant.dimen_10,
81 | placeholderImage: ImageConstant.imageDefault,
82 | width: 80,
83 | height: 80,
84 | ),
85 | VipText("(设置圆角)", marginTop: DimenConstant.dimen_5),
86 | ])),
87 | );
88 | }
89 |
90 | @override
91 | String getTitle() {
92 | return "Image";
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/lib/ui/widget/vip_image.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | import '../../base/base_widget.dart';
6 | import '../../constants/image_constant.dart';
7 |
8 | ///AUTHOR:AbnerMing
9 | ///DATE:2023/5/16
10 | ///INTRODUCE:图片控件
11 |
12 | class VipImage extends BaseWidget {
13 | final String? placeholderImage; //占位图
14 | final String? errorImage; //错误图
15 | final String? imagePath; //图片地址
16 | final BoxFit? imageBoxFit; //图片拉伸方式
17 | final int? imageLoadType; //图片加载类型
18 | final bool? isClipOval; //是否是圆角
19 | final double? imageRadius; //设置图片圆角
20 | const VipImage(this.imagePath,
21 | {super.key,
22 | this.placeholderImage,
23 | this.errorImage,
24 | this.imageBoxFit,
25 | this.imageLoadType,
26 | this.isClipOval,
27 | this.imageRadius,
28 | super.width,
29 | super.height,
30 | super.margin,
31 | super.marginLeft,
32 | super.marginTop,
33 | super.marginRight,
34 | super.marginBottom,
35 | super.padding,
36 | super.paddingLeft,
37 | super.paddingTop,
38 | super.paddingRight,
39 | super.paddingBottom,
40 | super.backgroundColor,
41 | super.strokeWidth,
42 | super.strokeColor,
43 | super.solidColor,
44 | super.radius,
45 | super.isCircle,
46 | super.leftTopRadius,
47 | super.rightTopRadius,
48 | super.leftBottomRadius,
49 | super.rightBottomRadius,
50 | super.childWidget,
51 | super.alignment,
52 | super.gradient,
53 | super.gradientBegin,
54 | super.gradientEnd,
55 | super.gradientColorList,
56 | super.gradientColorStops,
57 | super.onClick,
58 | super.onDoubleClick,
59 | super.onLongPress});
60 |
61 | @override
62 | Widget getWidget(BuildContext context) {
63 | //不需要占位图
64 | if (placeholderImage == null) {
65 | return getEndWidget(getImage());
66 | } else {
67 | return getEndWidget(getFadeInImage());
68 | }
69 | }
70 |
71 | /*
72 | * 返回最终的Widget
73 | * */
74 | Widget getEndWidget(widget) {
75 | //设置图片为圆形
76 | if (isClipOval == true) {
77 | return ClipOval(child: widget);
78 | }
79 | //设置图片圆角
80 | if (imageRadius != null) {
81 | return ClipRRect(
82 | borderRadius: BorderRadius.circular(imageRadius!), child: widget);
83 | }
84 | return widget;
85 | }
86 |
87 | /*
88 | * 有无占位图组件
89 | * */
90 | Widget getFadeInImage() {
91 | return FadeInImage(
92 | fit: imageBoxFit ?? BoxFit.contain,
93 | placeholderFit: imageBoxFit ?? BoxFit.contain,
94 | placeholder: getPlaceholder(),
95 | image: getImageProvider(),
96 | placeholderErrorBuilder: (ctx, err, stackTrace) => _imagePlaceholder(),
97 | imageErrorBuilder: (ctx, err, stackTrace) => _imageError());
98 | }
99 |
100 | /*
101 | * 无占位图组件
102 | * */
103 | Widget getImage() {
104 | return Image(
105 | image: getImageProvider(),
106 | fit: imageBoxFit ?? BoxFit.contain,
107 | errorBuilder: (ctx, err, stackTrace) => _imageError());
108 | }
109 |
110 | /*
111 | * 占位图错误组件
112 | * */
113 | Widget _imagePlaceholder() {
114 | return Image.asset(ImageConstant.imageDefault,
115 | fit: imageBoxFit ?? BoxFit.contain);
116 | }
117 |
118 | /*
119 | * 错误组件
120 | * */
121 | Widget _imageError() {
122 | var imagePath = ImageConstant.imageDefault;
123 | if (errorImage != null) {
124 | imagePath = errorImage!;
125 | }
126 | return Image.asset(imagePath, fit: imageBoxFit ?? BoxFit.contain);
127 | }
128 |
129 | /*
130 | * 判断图片是网络还是本地还是应用内
131 | * */
132 | ImageProvider getImageProvider() {
133 | if (imageLoadType == null) {
134 | return NetworkImage(imagePath!);
135 | } else if (imageLoadType == 0) {
136 | return FileImage(File(imagePath!));
137 | } else {
138 | return AssetImage(imagePath!);
139 | }
140 | }
141 |
142 | /*
143 | * 占位图
144 | * */
145 | ImageProvider getPlaceholder() {
146 | return AssetImage(placeholderImage!);
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_widget
2 | description: Flutter组件封装,包含了文本、图片、轮播图、网络、列表、tab选项卡等等功能,长期更新,不定期维护!
3 | # The following line prevents the package from being accidentally published to
4 | # pub.dev using `flutter pub publish`. This is preferred for private packages.
5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
6 |
7 | # The following defines the version and build number for your application.
8 | # A version number is three numbers separated by dots, like 1.2.43
9 | # followed by an optional build number separated by a +.
10 | # Both the version and the builder number may be overridden in flutter
11 | # build by specifying --build-name and --build-number, respectively.
12 | # In Android, build-name is used as versionName while build-number used as versionCode.
13 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
14 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
15 | # Read more about iOS versioning at
16 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
17 | # In Windows, build-name is used as the major, minor, and patch parts
18 | # of the product and file versions while build-number is used as the build suffix.
19 | version: 1.0.0+1
20 |
21 | environment:
22 | sdk: '>=2.19.6 <3.0.0'
23 |
24 | # Dependencies specify other packages that your package needs in order to work.
25 | # To automatically upgrade your package dependencies to the latest versions
26 | # consider running `flutter pub upgrade --major-versions`. Alternatively,
27 | # dependencies can be manually updated by changing the version numbers below to
28 | # the latest version available on pub.dev. To see which dependencies have newer
29 | # versions available, run `flutter pub outdated`.
30 | dependencies:
31 | flutter:
32 | sdk: flutter
33 |
34 |
35 | # The following adds the Cupertino Icons font to your application.
36 | # Use with the CupertinoIcons class for iOS style icons.
37 | cupertino_icons: ^1.0.2
38 | intl: ^0.18.1
39 |
40 | dev_dependencies:
41 | flutter_test:
42 | sdk: flutter
43 |
44 | # The "flutter_lints" package below contains a set of recommended lints to
45 | # encourage good coding practices. The lint set provided by the package is
46 | # activated in the `analysis_options.yaml` file located at the root of your
47 | # package. See that file for information about deactivating specific lint
48 | # rules and activating additional ones.
49 | flutter_lints: ^2.0.0
50 |
51 | # For information on the generic Dart part of this file, see the
52 | # following page: https://dart.dev/tools/pub/pubspec
53 |
54 | # The following section is specific to Flutter packages.
55 | flutter:
56 |
57 | # The following line ensures that the Material Icons font is
58 | # included with your application, so that you can use the icons in
59 | # the material Icons class.
60 | uses-material-design: true
61 |
62 | # To add assets to your application, add an assets section, like this:
63 | # assets:
64 | # - images/a_dot_burr.jpeg
65 | # - images/a_dot_ham.jpeg
66 |
67 | # An image asset can refer to one or more resolution-specific "variants", see
68 | # https://flutter.dev/assets-and-images/#resolution-aware
69 |
70 | # For details regarding adding assets from package dependencies, see
71 | # https://flutter.dev/assets-and-images/#from-packages
72 |
73 | # To add custom fonts to your application, add a fonts section here,
74 | # in this "flutter" section. Each entry in this list should have a
75 | # "family" key with the font family name, and a "fonts" key with a
76 | # list giving the asset and other descriptors for the font. For
77 | # example:
78 | # fonts:
79 | # - family: Schyler
80 | # fonts:
81 | # - asset: fonts/Schyler-Regular.ttf
82 | # - asset: fonts/Schyler-Italic.ttf
83 | # style: italic
84 | # - family: Trajan Pro
85 | # fonts:
86 | # - asset: fonts/TrajanPro.ttf
87 | # - asset: fonts/TrajanPro_Bold.ttf
88 | # weight: 700
89 | #
90 | # For details regarding fonts from package dependencies,
91 | # see https://flutter.dev/custom-fonts/#from-packages
92 | assets:
93 | - images/
94 |
--------------------------------------------------------------------------------
/lib/ui/widget/vip_app_bar_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'vip_text.dart';
4 |
5 | ///AUTHOR:AbnerMing
6 | ///DATE:2023/5/17
7 | ///INTRODUCE:AppBar组件
8 |
9 | class VipAppBarView extends AppBar {
10 | final VoidCallback? onLeftClick; //点击事件
11 | final double? barHeight; //标题栏的高度
12 | final String barTitle; //标题
13 | final Color? barTitleColor; //标题颜色
14 | final double? barTitleSize; //标题大小
15 | final bool? barTitleBold; //标题是否加粗
16 | final bool? barCenterTitle; //标题位置
17 | final String? barLeftIcon; //左边的Icon
18 | final Color? barBackgroundColor; //背景颜色
19 | final List? barRightListMenu; //右边的按钮
20 | final String? barRightMenuText; //右边的文字按钮
21 | final String? barRightMenuImage; //右边的图片按钮
22 | final VoidCallback? onRightMenuTextClick; //右边点击事件
23 | final VoidCallback? onRightMenuImageClick; //右边图片点击事件
24 | final Color? barRightMenuTextColor; //右边文字按钮颜色
25 | final double? barRightMenuTextSize; //右边文字按钮大小
26 | final bool? barRightMenuTextBold; //右边文字按钮是否加粗
27 | final bool? barIsClose; //左边关闭按钮
28 | final String? barLeftClose; //左边的关闭Icon
29 | final VoidCallback? onLeftCloseClick; //左侧关闭事件
30 |
31 | VipAppBarView(this.barTitle,
32 | {this.onLeftClick,
33 | this.barHeight = 44,
34 | this.barCenterTitle = true,
35 | this.barTitleColor = Colors.white,
36 | this.barTitleSize = 16,
37 | this.barLeftIcon,
38 | this.barTitleBold = false,
39 | this.barBackgroundColor,
40 | this.barRightListMenu,
41 | this.barRightMenuText,
42 | this.onRightMenuTextClick,
43 | this.barRightMenuImage,
44 | this.onRightMenuImageClick,
45 | this.barRightMenuTextColor = Colors.white,
46 | this.barRightMenuTextSize = 14,
47 | this.barRightMenuTextBold = false,
48 | this.barIsClose = false,
49 | this.barLeftClose,
50 | this.onLeftCloseClick,
51 | super.key});
52 |
53 | /*
54 | * 标题
55 | * */
56 | @override
57 | Widget? get title => VipText(barTitle,
58 | style: TextStyle(
59 | color: barTitleColor,
60 | fontSize: barTitleSize,
61 | fontWeight: barTitleBold! ? FontWeight.bold : FontWeight.normal));
62 |
63 | /*
64 | * 标题是否居中
65 | * */
66 | @override
67 | bool? get centerTitle => barCenterTitle;
68 |
69 | @override
70 | double? get toolbarHeight => barHeight;
71 |
72 | /*
73 | * 左边的icon
74 | * */
75 | @override
76 | Widget? get leading => getLeftWidget();
77 |
78 | /*
79 | * 右边的按钮
80 | * */
81 |
82 | @override
83 | List? get actions => getRightMenu();
84 |
85 | /*
86 | * 背景颜色
87 | * */
88 | @override
89 | Color? get backgroundColor => barBackgroundColor;
90 |
91 | /*
92 | * 返回右边的按钮
93 | * */
94 | List? getRightMenu() {
95 | List widgets = [];
96 | if (barRightMenuText != null) {
97 | widgets.add(IconButton(
98 | icon: VipText(barRightMenuText!,
99 | style: TextStyle(
100 | color: barRightMenuTextColor,
101 | fontSize: barRightMenuTextSize,
102 | fontWeight: barRightMenuTextBold!
103 | ? FontWeight.bold
104 | : FontWeight.normal)),
105 | onPressed: onRightMenuTextClick,
106 | ));
107 | }
108 | if (barRightMenuImage != null) {
109 | widgets.add(IconButton(
110 | icon: Image.asset(barRightMenuImage!),
111 | onPressed: onRightMenuImageClick,
112 | ));
113 | }
114 | //添加全部
115 | if (barRightListMenu != null) {
116 | widgets.addAll(barRightListMenu!);
117 | }
118 | return widgets;
119 | }
120 |
121 | /*
122 | * 返回左边的icon
123 | * */
124 | Widget getLeftWidget() {
125 | List leftWidgets = [];
126 |
127 | leftWidgets.add(Expanded(
128 | flex: 1,
129 | child: IconButton(
130 | icon: barLeftIcon != null
131 | ? Image.asset(barLeftIcon!)
132 | : const Icon(Icons.arrow_back_ios),
133 | onPressed: onLeftClick,
134 | )));
135 |
136 | if (barIsClose!) {
137 | //需要关闭按钮
138 | leftWidgets.add(Expanded(
139 | flex: 1,
140 | child: IconButton(
141 | icon: barLeftClose != null
142 | ? Image.asset(barLeftClose!)
143 | : const Icon(Icons.close),
144 | onPressed: onLeftCloseClick,
145 | )));
146 | }
147 | return Row(
148 | children: leftWidgets,
149 | );
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/lib/utils/popup_window.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | ///AUTHOR:AbnerMing
4 | ///DATE:2023/8/16
5 | ///INTRODUCE:窗口弹出
6 |
7 | class PopupWindow {
8 | //加到屏幕上
9 | static OverlayEntry? _overlayEntry;
10 |
11 | static void create(
12 | GlobalKey? key,
13 | Widget child, {
14 | double childWidth = 0.0, //子组件的宽
15 | double childHeight = 0.0, //子组件的高
16 | WindowDirection direction = WindowDirection.bottom,
17 | double left = 0.0,
18 | double top = 0.0,
19 | double margin = 0.0,
20 | }) {
21 | if (key == null) {
22 | return;
23 | }
24 |
25 | var size = getWidgetSize(key); //获取在目标组件的位置
26 |
27 | double widgetTop = 0.0;
28 | double widgetLeft = 0.0;
29 | switch (direction) {
30 | case WindowDirection.bottom: //下面
31 | widgetTop = size.bottom + margin;
32 | widgetLeft =
33 | size.right - childWidth / 2 - ((size.right - size.left) / 2);
34 | break;
35 | case WindowDirection.left: //左面
36 | widgetTop =
37 | size.bottom - childHeight / 2 - ((size.bottom - size.top) / 2);
38 | widgetLeft = size.left - childWidth - margin;
39 | break;
40 | case WindowDirection.top: //上面
41 | widgetTop = size.top - childHeight - margin;
42 | widgetLeft =
43 | size.right - childWidth / 2 - ((size.right - size.left) / 2);
44 | break;
45 | case WindowDirection.right: //右面
46 | widgetTop =
47 | size.bottom - childHeight / 2 - ((size.bottom - size.top) / 2);
48 | widgetLeft = size.right + margin;
49 | break;
50 | case WindowDirection.topLeft: //左上
51 | widgetTop =
52 | size.bottom - childHeight - (size.bottom - size.top) - margin;
53 | widgetLeft = size.left - childWidth - margin;
54 | break;
55 | case WindowDirection.topRight: //右上
56 | widgetTop =
57 | size.bottom - childHeight - (size.bottom - size.top) - margin;
58 | widgetLeft = size.right + margin;
59 | break;
60 | case WindowDirection.bottomLeft: //左下
61 | widgetTop = size.bottom + margin;
62 | widgetLeft = size.left - childWidth - margin;
63 | break;
64 | case WindowDirection.bottomRight: //右下
65 | widgetTop = size.bottom + margin;
66 | widgetLeft = size.right + margin;
67 |
68 | break;
69 | case WindowDirection.none: //取消 自己测量位置
70 | widgetTop = top;
71 | widgetLeft = left;
72 | break;
73 | }
74 |
75 | //获取OverlayState
76 | OverlayState overlayState = Overlay.of(key.currentContext!);
77 | if (_overlayEntry == null) {
78 | //通过OverlayEntry将构建的布局插入到整个布局的最上层
79 | _overlayEntry = OverlayEntry(
80 | builder: (BuildContext context) => GestureDetector(
81 | onTap: () {
82 | hint();
83 | },
84 | child: Container(
85 | color: Colors.transparent,
86 | child: Material(
87 | color: Colors.transparent,
88 | child: Stack(
89 | children: [
90 | Positioned(
91 | top: widgetTop,
92 | left: widgetLeft,
93 | child: child,
94 | )
95 | ],
96 | ),
97 | ))));
98 | //插入到整个布局的最上层
99 | overlayState.insert(_overlayEntry!);
100 | } else {
101 | //重新绘制UI,类似setState
102 | _overlayEntry?.markNeedsBuild();
103 | }
104 | }
105 |
106 | ///获取组件的位置
107 | static WidgetSize getWidgetSize(GlobalKey key) {
108 | //获取组件的位置,在左上右下
109 | final RenderBox renderBox =
110 | (key.currentContext?.findRenderObject() as RenderBox);
111 | final left = renderBox.localToGlobal(Offset.zero).dx; //左边
112 | final top = renderBox.localToGlobal(Offset(renderBox.size.width, 0)).dy;
113 | final bottom = renderBox.localToGlobal(Offset(0, renderBox.size.height)).dy;
114 | final right = renderBox
115 | .localToGlobal(Offset(renderBox.size.width, renderBox.size.height))
116 | .dx;
117 | return WidgetSize(left, top, right, bottom);
118 | }
119 |
120 | ///隐藏
121 | static void hint() {
122 | _overlayEntry?.remove();
123 | _overlayEntry = null;
124 | }
125 | }
126 |
127 | ///组件对象,标记左上右下
128 | class WidgetSize {
129 | double left;
130 | double top;
131 | double right;
132 | double bottom;
133 |
134 | WidgetSize(this.left, this.top, this.right, this.bottom);
135 | }
136 |
137 | ///弹出方向
138 | enum WindowDirection {
139 | left,
140 | top,
141 | right,
142 | bottom,
143 | topLeft, //左上角
144 | topRight, //右上角
145 | bottomLeft, //左下
146 | bottomRight, //右下
147 | none,
148 | }
149 |
--------------------------------------------------------------------------------
/lib/ui/page/view/bar/tab_bar_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../../base/base_page_state.dart';
4 | import '../../../../constants/dimen_constant.dart';
5 | import '../../../widget/vip_tab_bar_view.dart';
6 | import '../../../widget/vip_text.dart';
7 |
8 | ///AUTHOR:AbnerMing
9 | ///DATE:2023/5/18
10 | ///INTRODUCE:TabBar组件效果页面
11 |
12 | class TabBarPage extends StatefulWidget {
13 | const TabBarPage({super.key});
14 |
15 | @override
16 | State createState() => _TabBarPageState();
17 | }
18 |
19 | class _TabBarPageState extends BasePageState {
20 | @override
21 | Widget getBody(BuildContext context) {
22 | var tabs = ["条目一", "条目二", "条目三", "条目四", "条目五", "条目六", "条目七", "条目八"];
23 | var tabs2 = ["条目一", "条目二", "条目三"];
24 | var tabImages = [
25 | "https://www.vipandroid.cn/ming/pic/new_java.png",
26 | "https://www.vipandroid.cn/ming/pic/new_android.png",
27 | "https://www.vipandroid.cn/ming/pic/new_kotlin.png"
28 | ]; //图片指示器
29 | var bodyList = tabs
30 | .map((e) => VipText(e, backgroundColor: Colors.amberAccent))
31 | .toList();
32 | var bodyList2 = tabs2
33 | .map((e) => VipText(e, backgroundColor: Colors.amberAccent))
34 | .toList();
35 | return Column(children: [
36 | const VipText(
37 | "多个Tab滑动",
38 | alignment: Alignment.topLeft,
39 | marginTop: DimenConstant.dimen_10,
40 | style: TextStyle(fontWeight: FontWeight.bold),
41 | ),
42 | SizedBox(
43 | height: 80,
44 | child: VipTabBarView(
45 | tabTitleList: tabs,
46 | tabBodyList: bodyList,
47 | )),
48 | const VipText("固定Tab不滑动",
49 | alignment: Alignment.topLeft,
50 | marginTop: DimenConstant.dimen_10,
51 | style: TextStyle(fontWeight: FontWeight.bold)),
52 | SizedBox(
53 | height: 80,
54 | child: VipTabBarView(
55 | tabTitleList: tabs2,
56 | tabBodyList: bodyList2,
57 | isScrollable: false,
58 | )),
59 | const VipText("修改指示器颜色",
60 | alignment: Alignment.topLeft,
61 | marginTop: DimenConstant.dimen_10,
62 | style: TextStyle(fontWeight: FontWeight.bold)),
63 | SizedBox(
64 | height: 80,
65 | child: VipTabBarView(
66 | tabTitleList: tabs2,
67 | tabBodyList: bodyList2,
68 | isScrollable: false,
69 | labelColor: Colors.red,
70 | unselectedLabelColor: Colors.black,
71 | indicatorColor: Colors.red,
72 | )),
73 | const VipText("修改指示器大小",
74 | alignment: Alignment.topLeft,
75 | marginTop: DimenConstant.dimen_10,
76 | style: TextStyle(fontWeight: FontWeight.bold)),
77 | SizedBox(
78 | height: 80,
79 | child: VipTabBarView(
80 | tabTitleList: tabs2,
81 | tabBodyList: bodyList2,
82 | isScrollable: false,
83 | labelColor: Colors.red,
84 | unselectedLabelColor: Colors.black,
85 | indicatorColor: Colors.red,
86 | indicatorSize: TabBarIndicatorSize.label,
87 | )),
88 | const VipText("图片指示器",
89 | alignment: Alignment.topLeft,
90 | marginTop: DimenConstant.dimen_10,
91 | style: TextStyle(fontWeight: FontWeight.bold)),
92 | SizedBox(
93 | height: 80,
94 | child: VipTabBarView(
95 | tabImageList: tabImages,
96 | tabBodyList: bodyList2,
97 | isScrollable: false,
98 | labelColor: Colors.red,
99 | unselectedLabelColor: Colors.black,
100 | indicatorColor: Colors.red,
101 | indicatorSize: TabBarIndicatorSize.label,
102 | )),
103 | const VipText("左图右文指示器",
104 | alignment: Alignment.topLeft,
105 | marginTop: DimenConstant.dimen_10,
106 | style: TextStyle(fontWeight: FontWeight.bold)),
107 | SizedBox(
108 | height: 80,
109 | child: VipTabBarView(
110 | tabIconAndTextList: [
111 | VipTabBarBean(
112 | "Java", "https://www.vipandroid.cn/ming/pic/new_java.png"),
113 | VipTabBarBean("Android",
114 | "https://www.vipandroid.cn/ming/pic/new_android.png"),
115 | VipTabBarBean("Kotlin",
116 | "https://www.vipandroid.cn/ming/pic/new_kotlin.png"),
117 | ],
118 | tabBodyList: bodyList2,
119 | isScrollable: false,
120 | labelColor: Colors.red,
121 | unselectedLabelColor: Colors.black,
122 | indicatorColor: Colors.red,
123 | indicatorSize: TabBarIndicatorSize.label,
124 | ))
125 | ]);
126 | }
127 |
128 | @override
129 | String getTitle() {
130 | return "TabBar";
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/lib/utils/time_util.dart:
--------------------------------------------------------------------------------
1 | import 'package:intl/intl.dart';
2 |
3 | ///AUTHOR:AbnerMing
4 | ///DATE:2023/6/25
5 | ///INTRODUCE:时间工具类
6 | class TimeUtil {
7 | static const String y_M_d = "yyyy-MM-dd"; //年月日
8 | static const String y_M_d_w = "yyyy年MM月dd日"; //年月日
9 | static const String y_M_d_i = "yyyy/MM/dd"; //年月日
10 | static const String y_M_d_h_m_s = "yyyy-MM-dd hh:mm:ss"; //年月日时分秒
11 | static const String y_M_d_h_m_s_w = "yyyy年MM月dd日 hh时mm分ss秒"; //年月日时分秒
12 | static const String y_M_d_h_m_s_i = "yyyy/MM/dd hh:mm:ss"; //年月日时分秒
13 | static const String M_d = "MM-dd"; //月日
14 | static const String h_m_s = "hh:mm:ss"; //时分秒
15 | static const String m_s = "mm:ss"; //分秒
16 |
17 | /*
18 | * 获取当前时间戳,返回为毫秒
19 | * */
20 | static int getTimeStamp() {
21 | return DateTime.now().millisecondsSinceEpoch;
22 | }
23 |
24 | /*
25 | * 获取当前时间 DateTime格式
26 | * */
27 | static DateTime getDateTimeNow() {
28 | return DateTime.now();
29 | }
30 |
31 | /*
32 | * 日期转时间戳
33 | * `"2012-02-27"`
34 | * `"2012-02-27 13:27:00"`
35 | * `"2012-02-27 13:27:00.123456789z"`
36 | * `"2012-02-27 13:27:00,123456789z"`
37 | * `"20120227 13:27:00"`
38 | * `"20120227T132700"`
39 | * `"20120227"`
40 | * `"+20120227"`
41 | * `"2012-02-27T14Z"`
42 | * `"2012-02-27T14+00:00"`
43 | * `"-123450101 00:00:00 Z"`: in the year -12345.
44 | * `"2002-02-27T14:00:00-0500"`: Same as `"2002-02-27T19:00:00Z"`
45 | * */
46 | static int getStringDateToTimeStamp(String date) {
47 | DateTime dateTime = getTimeStampByDataTime(date);
48 | return dateTime.millisecondsSinceEpoch;
49 | }
50 |
51 | /*
52 | * 获取当前日期,返回指定格式
53 | * */
54 | static String getNowDateTimeFormat(String outFormat) {
55 | var format = DateFormat(outFormat);
56 | DateTime date = DateTime.now();
57 | String formatResult = format.format(date);
58 | return formatResult;
59 | }
60 |
61 | /*
62 | * 获取以往日期的时间戳,默认返回昨天
63 | * */
64 | static int getBeforeTimeStamp({int day = 1}) {
65 | var dateTime = DateTime.fromMillisecondsSinceEpoch(
66 | DateTime.now().millisecondsSinceEpoch - (24 * 60 * 60 * 1000 * day));
67 | return dateTime.millisecondsSinceEpoch;
68 | }
69 |
70 | /*
71 | * 获取以往日期的时间,可以是昨天,前天,等等
72 | * */
73 | static String getBeforeDay(String outFormat, {int day = 1}) {
74 | var dateTime = DateTime.fromMillisecondsSinceEpoch(
75 | DateTime.now().millisecondsSinceEpoch - (24 * 60 * 60 * 1000 * day));
76 | var format = DateFormat(outFormat);
77 | return format.format(dateTime);
78 | }
79 |
80 | /*
81 | * 时间戳转时间
82 | * */
83 | static String getTimeStampToStringDate(int timeStamp,
84 | {String format = y_M_d}) {
85 | var dateFormat = DateFormat(format);
86 | var dateTime = DateTime.fromMillisecondsSinceEpoch(timeStamp);
87 | return dateFormat.format(dateTime);
88 | }
89 |
90 | /*
91 | * 获取两个日期之间间隔天数
92 | * */
93 | static int getRangeDayNumber(DateTime startData, DateTime endData) {
94 | if (startData.millisecondsSinceEpoch > endData.millisecondsSinceEpoch) {
95 | return startData.difference(endData).inDays;
96 | }
97 | return endData.difference(startData).inDays;
98 | }
99 |
100 | /*
101 | * 和当前时间间隔,时间戳模式
102 | * */
103 |
104 | static String getRangeTimeNow(int oldTime) {
105 | //获取当前的时间戳
106 | var startTimeStamp = getTimeStamp();
107 | var oldTimeStamp = startTimeStamp - oldTime;
108 | var startData = DateTime.fromMillisecondsSinceEpoch(startTimeStamp);
109 | var oldData = DateTime.fromMillisecondsSinceEpoch(oldTimeStamp);
110 | var difference = startData.difference(oldData);
111 | var seconds = difference.inSeconds;
112 | if (seconds < 60) {
113 | return "$seconds秒前";
114 | }
115 | var minutes = difference.inMinutes;
116 | if (minutes < 60) {
117 | return "$minutes分钟前";
118 | }
119 | var hours = difference.inHours;
120 | if (hours < 24) {
121 | return "$hours小时前";
122 | }
123 | var days = difference.inDays;
124 | print(days);
125 | if (days < 365) {
126 | return "$days天前";
127 | }
128 | return getTimeStampToStringDate(oldTime);
129 | }
130 |
131 | /*
132 | * 日期转DateTime
133 | * */
134 | static DateTime getTimeStampByDataTime(String date) {
135 | if (date.contains("年") || date.contains("月") || date.contains("/")) {
136 | date = date.replaceAll(RegExp(r'年|月|/'), "-");
137 | }
138 | if (date.contains("时") || date.contains("分")) {
139 | date = date.replaceAll(RegExp(r'时|分'), "-");
140 | }
141 | if (date.contains("日") || date.contains("秒")) {
142 | date = date.replaceAll(RegExp(r'日|秒'), "");
143 | }
144 |
145 | return DateTime.parse(date);
146 | }
147 |
148 | /*
149 | * 获取星期几,int类型
150 | * */
151 | static int getWeekNumber(DateTime date) {
152 | var week = date.weekday;
153 | return week;
154 | }
155 |
156 | /*
157 | * 获取星期几
158 | * */
159 | static String getWeek(DateTime date, {String tag = "星期"}) {
160 | var week = date.weekday;
161 | String w = '';
162 | switch (week.toString()) {
163 | case '1':
164 | w = '一';
165 | break;
166 | case '2':
167 | w = '二';
168 | break;
169 | case '3':
170 | w = '三';
171 | break;
172 | case '4':
173 | w = '四';
174 | break;
175 | case '5':
176 | w = '五';
177 | break;
178 | case '6':
179 | w = '六';
180 | break;
181 | case '7':
182 | w = '日';
183 | break;
184 | }
185 | return '$tag$w';
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/lib/ui/widget/vip_tab_bar_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../constants/dimen_constant.dart';
4 | import 'vip_text.dart';
5 |
6 | ///AUTHOR:AbnerMing
7 | ///DATE:2023/5/18
8 | ///INTRODUCE:TabBar组件
9 |
10 | class VipTabBarView extends StatefulWidget {
11 | final List? tabTitleList; //tab指示器的标题集合,文字形式
12 | final List? tabImageList; //tab指示器的标题集合,图片形式
13 | final List? tabWidgetList; //tab指示器的标题集合,Widget形式
14 | final List? tabIconAndTextList; //tab指示器的标题集合,左图右文形式
15 | final List? tabBodyList; //tab指示器的页面
16 | final Function(int)? onPageChange; //页面滑动回调
17 | final Color? indicatorColor; //指示器的颜色
18 | final Color? labelColor; //标签的颜色
19 | final Color? unselectedLabelColor; //未选中标签的颜色
20 | final TabBarIndicatorSize? indicatorSize; //指示器的大小 是和文字宽度一样还是充满
21 | final double? indicatorHeight; //指示器的高度
22 | final bool? isScrollable; //指示器是否支持滑动
23 | final double? tabImageWidth; //图片指示器的宽 仅用于图片指示器和图文指示器
24 | final double? tabImageHeight; //图片指示器的高 仅用于图片指示器和图文指示器
25 | final double? tabIconAndTextMargin; //左图右文指示器,icon距离文字的距离
26 | final double? tabHeight; //tab高度
27 |
28 | const VipTabBarView(
29 | {this.tabTitleList,
30 | this.tabImageList,
31 | this.tabWidgetList,
32 | this.tabIconAndTextList,
33 | this.tabBodyList,
34 | this.onPageChange,
35 | this.indicatorColor = Colors.black,
36 | this.labelColor = Colors.black,
37 | this.unselectedLabelColor = Colors.grey,
38 | this.indicatorSize = TabBarIndicatorSize.tab,
39 | this.indicatorHeight = 2,
40 | this.isScrollable = true,
41 | this.tabImageWidth = DimenConstant.dimen_15,
42 | this.tabImageHeight = DimenConstant.dimen_15,
43 | this.tabIconAndTextMargin = DimenConstant.dimen_5,
44 | this.tabHeight = DimenConstant.dimen_44,
45 | super.key});
46 |
47 | @override
48 | State createState() => _VipTabBarViewState();
49 | }
50 |
51 | ///左图右文的对象
52 | class VipTabBarBean {
53 | String title;
54 | String icon;
55 |
56 | VipTabBarBean(this.title, this.icon);
57 | }
58 |
59 | class _VipTabBarViewState extends State
60 | with SingleTickerProviderStateMixin {
61 | // 标签控制器
62 | late TabController _tabController;
63 |
64 | @override
65 | void initState() {
66 | super.initState();
67 | // 定义控制器
68 | _tabController = TabController(
69 | vsync: this,
70 | length: widget.tabBodyList != null ? widget.tabBodyList!.length : 0,
71 | );
72 | // 添加监听事件
73 | _tabController.addListener(() {
74 | //滑动的索引
75 | if (widget.onPageChange != null) {
76 | widget.onPageChange!(_tabController.index);
77 | }
78 | });
79 | }
80 |
81 | @override
82 | void dispose() {
83 | super.dispose();
84 | // 杀死控制器
85 | _tabController.dispose();
86 | }
87 |
88 | /*
89 | * 指示器点击
90 | */
91 | void onPage(position) {}
92 |
93 | @override
94 | Widget build(BuildContext context) {
95 | List tabList = []; //tab指示器
96 | List bodyList = []; //tab指示器对应的页面
97 | //文字形式
98 | if (widget.tabTitleList != null) {
99 | tabList = widget.tabTitleList!
100 | .map((e) => Tab(
101 | text: e,
102 | height: widget.tabHeight,
103 | ))
104 | .toList();
105 | }
106 | //图片形式
107 | if (widget.tabImageList != null) {
108 | tabList = widget.tabImageList!.map((e) {
109 | Widget view;
110 | if (e.contains("http")) {
111 | //网络图片
112 | view = Image.network(
113 | e,
114 | width: widget.tabImageWidth,
115 | height: widget.tabImageHeight,
116 | );
117 | } else {
118 | view = Image.asset(
119 | e,
120 | width: widget.tabImageWidth,
121 | height: widget.tabImageHeight,
122 | );
123 | }
124 | return Tab(icon: view, height: widget.tabHeight);
125 | }).toList();
126 | }
127 | //自定义Widget
128 | if (widget.tabWidgetList != null) {
129 | tabList = widget.tabWidgetList!;
130 | }
131 | //左图右文形式
132 | if (widget.tabIconAndTextList != null) {
133 | tabList = widget.tabIconAndTextList!.map((e) {
134 | return VipText(
135 | e.title,
136 | leftIcon: e.icon,
137 | height: widget.tabHeight,
138 | leftIconWidth: widget.tabImageWidth,
139 | leftIconHeight: widget.tabImageHeight,
140 | iconMarginRight: widget.tabIconAndTextMargin,
141 | );
142 | }).toList();
143 | }
144 |
145 | //指示器对应的页面
146 | if (widget.tabBodyList != null) {
147 | bodyList = widget.tabBodyList!.map((e) => e).toList();
148 | }
149 |
150 | return Scaffold(
151 | appBar: TabBar(
152 | // 加上控制器
153 | controller: _tabController,
154 | tabs: tabList,
155 | // 标签指示器的颜色
156 | indicatorColor: widget.indicatorColor,
157 | // 标签的颜色
158 | labelColor: widget.labelColor,
159 | // 未选中标签的颜色
160 | unselectedLabelColor: widget.unselectedLabelColor,
161 | // 指示器的大小
162 | indicatorSize: widget.indicatorSize,
163 | // 指示器的权重,即线条高度
164 | indicatorWeight: widget.indicatorHeight!,
165 | // 多个标签时滚动加载
166 | isScrollable: widget.isScrollable!,
167 | onTap: onPage,
168 | ),
169 | body: TabBarView(
170 | // 加上控制器
171 | controller: _tabController,
172 | children: bodyList,
173 | ),
174 | );
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/lib/base/base_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | ///AUTHOR:AbnerMing
4 | ///DATE:2023/5/11
5 | ///INTRODUCE:控件无状态基类
6 |
7 | class BaseWidget extends StatelessWidget {
8 | final VoidCallback? onClick; //点击事件
9 | final VoidCallback? onDoubleClick; //双击事件
10 | final VoidCallback? onLongPress; //长按事件
11 | final double? width; //宽度
12 | final double? height; //高度
13 | final double? margin; //外边距,左上右下
14 | final double? marginLeft; //外边距,距离左边
15 | final double? marginTop; //外边距,距离上边
16 | final double? marginRight; //外边距,距离右边
17 | final double? marginBottom; //外边距,距离下边
18 | final double? padding; //内边距,左上右下
19 | final double? paddingLeft; //内边距,距离左边
20 | final double? paddingTop; //内边距,距离上边
21 | final double? paddingRight; //内边距,距离右边
22 | final double? paddingBottom; //内边距,距离下边
23 | final Color? backgroundColor; //背景颜色 和 decoration 二者取其一
24 | final double? strokeWidth; //背景边框统一的宽度
25 | final Color? strokeColor; //背景边框的颜色
26 | final Color? solidColor; //背景填充颜色
27 | final double? radius; //背景的角度
28 | final bool? isCircle; //背景是否是圆形
29 | final double? leftTopRadius; //背景左上角度
30 | final double? rightTopRadius; //背景 右上角度
31 | final double? leftBottomRadius; //背景 左下角度
32 | final double? rightBottomRadius; //背景 右下角度
33 | final Widget? childWidget; //子控件
34 | final Alignment? alignment; //位置
35 | final int? gradient; //渐变方式,为支持后续拓展,用int类型
36 | final List? gradientColorList; //渐变颜色
37 | final List? gradientColorStops; //颜色值梯度,取值范围[0,1]
38 | final Alignment? gradientBegin; //渐变起始位置
39 | final Alignment? gradientEnd; //渐变结束位置
40 |
41 | //边框的颜色
42 | const BaseWidget(
43 | {super.key,
44 | this.width,
45 | this.height,
46 | this.margin,
47 | this.marginLeft,
48 | this.marginTop,
49 | this.marginRight,
50 | this.marginBottom,
51 | this.padding,
52 | this.paddingLeft,
53 | this.paddingTop,
54 | this.paddingRight,
55 | this.paddingBottom,
56 | this.backgroundColor,
57 | this.strokeWidth,
58 | this.strokeColor,
59 | this.solidColor,
60 | this.radius,
61 | this.isCircle,
62 | this.leftTopRadius,
63 | this.rightTopRadius,
64 | this.leftBottomRadius,
65 | this.rightBottomRadius,
66 | this.childWidget,
67 | this.alignment,
68 | this.gradient,
69 | this.gradientColorList,
70 | this.gradientColorStops,
71 | this.gradientBegin,
72 | this.gradientEnd,
73 | this.onClick,
74 | this.onDoubleClick,
75 | this.onLongPress});
76 |
77 | @override
78 | Widget build(BuildContext context) {
79 | return InkWell(
80 | highlightColor: Colors.transparent,
81 | // 透明色
82 | splashColor: Colors.transparent,
83 | // 透明色
84 | onTap: onClick,
85 | onDoubleTap: onDoubleClick,
86 | onLongPress: onLongPress,
87 | child: Container(
88 | width: width,
89 | height: height,
90 | alignment: alignment,
91 | margin: margin != null
92 | ? EdgeInsets.all(margin!)
93 | : EdgeInsets.only(
94 | left: marginLeft != null ? marginLeft! : 0,
95 | top: marginTop != null ? marginTop! : 0,
96 | right: marginRight != null ? marginRight! : 0,
97 | bottom: marginBottom != null ? marginBottom! : 0),
98 | padding: padding != null
99 | ? EdgeInsets.all(padding!)
100 | : EdgeInsets.only(
101 | left: paddingLeft != null ? paddingLeft! : 0,
102 | top: paddingTop != null ? paddingTop! : 0,
103 | right: paddingRight != null ? paddingRight! : 0,
104 | bottom: paddingBottom != null ? paddingBottom! : 0,
105 | ),
106 | color: backgroundColor,
107 | decoration: backgroundColor != null ? null : getDecoration(),
108 | child: childWidget ?? getWidget(context),
109 | ));
110 | }
111 |
112 | /*
113 | * 获取Decoration
114 | * */
115 | Decoration? getDecoration() {
116 | BorderRadiusGeometry? borderRadiusGeometry;
117 | if (radius != null) {
118 | //所有的角度
119 | borderRadiusGeometry = BorderRadius.all(Radius.circular(radius!));
120 | } else {
121 | //否则就是,各个角度
122 | borderRadiusGeometry = BorderRadius.only(
123 | topLeft: Radius.circular(leftTopRadius != null ? leftTopRadius! : 0),
124 | topRight:
125 | Radius.circular(rightTopRadius != null ? rightTopRadius! : 0),
126 | bottomLeft:
127 | Radius.circular(leftBottomRadius != null ? leftBottomRadius! : 0),
128 | bottomRight: Radius.circular(
129 | rightBottomRadius != null ? rightBottomRadius! : 0));
130 | }
131 | Gradient? tGradient;
132 | if (gradient != null) {
133 | tGradient = LinearGradient(
134 | colors: gradientColorList != null ? gradientColorList! : [],
135 | // 设置有哪些渐变色
136 | begin: gradientBegin != null ? gradientBegin! : Alignment.centerLeft,
137 | // 渐变色开始的位置,默认 centerLeft
138 | end: gradientEnd != null ? gradientEnd! : Alignment.centerRight,
139 | // 渐变色结束的位置,默认 centerRight
140 | stops: gradientColorStops, // 颜色值梯度,取值范围[0,1],长度要和 colors 的长度一样
141 | );
142 | }
143 | Decoration? widgetDecoration = BoxDecoration(
144 | gradient: tGradient,
145 | //背景颜色
146 | color: solidColor != null ? solidColor! : Colors.transparent,
147 | //圆角半径
148 | borderRadius: isCircle == true ? null : borderRadiusGeometry,
149 | //是否是圆形
150 | shape: isCircle == true ? BoxShape.circle : BoxShape.rectangle,
151 | //边框线宽、颜色
152 | border: Border.all(
153 | width: strokeWidth != null ? strokeWidth! : 0,
154 | color: strokeColor != null ? strokeColor! : Colors.transparent),
155 | );
156 | return widgetDecoration;
157 | }
158 |
159 | /*
160 | * 获取控件
161 | * */
162 | Widget? getWidget(BuildContext context) {
163 | return null;
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/lib/ui/widget/vip_text.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/gestures.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import '../../base/base_widget.dart';
5 | import '../../constants/dimen_constant.dart';
6 | import '../../data/bean/text_rich_bean.dart';
7 |
8 | ///AUTHOR:AbnerMing
9 | ///DATE:2023/5/11
10 | ///INTRODUCE:文本控件 Text
11 |
12 | class VipText extends BaseWidget {
13 | final String text; //文本内容
14 | final TextStyle? style; //文本样式
15 | final String? leftIcon; //左边的图片
16 | final double? leftIconWidth; //左边的图片宽度
17 | final double? leftIconHeight; //左边的图片高度
18 | final double? iconMarginRight; //图片距离右边的文字距离
19 | final String? rightIcon; //右边的图片
20 | final double? rightIconWidth; //右边的图片宽度
21 | final double? rightIconHeight; //右边的图片高度
22 | final double? iconMarginLeft; //图片距离左边的文字距离
23 | final String? topIcon; //上边的图片
24 | final double? topIconWidth; //上边的图片宽度
25 | final double? topIconHeight; //上边的图片高度
26 | final double? iconMarginBottom; //图片距离下边的文字距离
27 | final String? bottomIcon; //下边的图片
28 | final double? bottomIconWidth; //下边的图片宽度
29 | final double? bottomIconHeight; //下边的图片高度
30 | final double? iconMarginTop; //图片距离上边的文字距离
31 | final MainAxisAlignment? mainAxisAlignment; //文字的位置
32 | final TextOverflow? textOverflow; //文字溢出方式
33 | final TextAlign? textAlign; //文字位置
34 | final int? maxLines; //最大行数
35 | final List? richList; //富文本数据
36 | final Function(int, TextRichBean)? onRichClick; //富文本点击事件
37 |
38 | const VipText(this.text,
39 | {super.key,
40 | this.style,
41 | this.leftIcon,
42 | this.leftIconWidth = DimenConstant.dimen_22,
43 | this.leftIconHeight = DimenConstant.dimen_22,
44 | this.iconMarginRight = 0,
45 | this.rightIcon,
46 | this.rightIconWidth = DimenConstant.dimen_22,
47 | this.rightIconHeight = DimenConstant.dimen_22,
48 | this.iconMarginLeft = 0,
49 | this.topIcon,
50 | this.topIconWidth = DimenConstant.dimen_22,
51 | this.topIconHeight = DimenConstant.dimen_22,
52 | this.iconMarginBottom = 0,
53 | this.bottomIcon,
54 | this.bottomIconWidth = DimenConstant.dimen_22,
55 | this.bottomIconHeight = DimenConstant.dimen_22,
56 | this.iconMarginTop = 0,
57 | this.mainAxisAlignment = MainAxisAlignment.center,
58 | this.textOverflow,
59 | this.textAlign,
60 | this.maxLines,
61 | this.richList,
62 | this.onRichClick,
63 | super.width,
64 | super.height,
65 | super.margin,
66 | super.marginLeft,
67 | super.marginTop,
68 | super.marginRight,
69 | super.marginBottom,
70 | super.padding,
71 | super.paddingLeft,
72 | super.paddingTop,
73 | super.paddingRight,
74 | super.paddingBottom,
75 | super.backgroundColor,
76 | super.strokeWidth,
77 | super.strokeColor,
78 | super.solidColor,
79 | super.radius,
80 | super.isCircle,
81 | super.leftTopRadius,
82 | super.rightTopRadius,
83 | super.leftBottomRadius,
84 | super.rightBottomRadius,
85 | super.childWidget,
86 | super.alignment,
87 | super.onClick,
88 | super.onDoubleClick,
89 | super.onLongPress});
90 |
91 | /*
92 | * 返回图片的组件
93 | * */
94 | Widget getImageWidget(String icon, double width, double height) {
95 | if (icon.contains("http")) {
96 | return Image.network(icon, width: width, height: height);
97 | } else {
98 | return Image.asset(icon, width: width, height: height);
99 | }
100 | }
101 |
102 | @override
103 | Widget getWidget(BuildContext context) {
104 | List widgets = [];
105 | //左边的Icon
106 | if (leftIcon != null) {
107 | widgets.add(getImageWidget(leftIcon!, leftIconWidth!, leftIconHeight!));
108 | }
109 | //水平中间的文字
110 | if (leftIcon != null || rightIcon != null) {
111 | widgets.add(Container(
112 | margin: EdgeInsets.only(left: iconMarginRight!, right: iconMarginLeft!),
113 | child: getTextWidget(),
114 | ));
115 | }
116 |
117 | //右边的Icon
118 | if (rightIcon != null) {
119 | widgets
120 | .add(getImageWidget(rightIcon!, rightIconWidth!, rightIconHeight!));
121 | }
122 |
123 | if (widgets.isNotEmpty) {
124 | return Row(
125 | mainAxisAlignment: mainAxisAlignment!,
126 | children: widgets,
127 | );
128 | }
129 |
130 | //上边的icon
131 | if (topIcon != null) {
132 | widgets.add(getImageWidget(topIcon!, topIconWidth!, topIconHeight!));
133 | }
134 | //垂直中间的文字
135 | if (topIcon != null || bottomIcon != null) {
136 | widgets.add(Container(
137 | margin: EdgeInsets.only(top: iconMarginBottom!, bottom: iconMarginTop!),
138 | child: getTextWidget(),
139 | ));
140 | }
141 | //下面的icon
142 | if (bottomIcon != null) {
143 | widgets.add(
144 | getImageWidget(bottomIcon!, bottomIconWidth!, bottomIconHeight!));
145 | }
146 | if (widgets.isNotEmpty) {
147 | return Column(
148 | mainAxisAlignment: mainAxisAlignment!,
149 | children: widgets,
150 | );
151 | }
152 |
153 | //富文本
154 | if (richList != null && richList!.isNotEmpty) {
155 | List list = [];
156 | for (var a = 0; a < richList!.length; a++) {
157 | var richBean = richList![a];
158 | var textSpan = TextSpan(
159 | text: richBean.text,
160 | recognizer: TapGestureRecognizer()
161 | ..onTap = () {
162 | //点击事件
163 | if (onRichClick != null) {
164 | onRichClick!(a, richBean);
165 | }
166 | },
167 | style: TextStyle(
168 | fontSize: richBean.textSize, color: richBean.textColor));
169 | list.add(textSpan);
170 | }
171 | //富文本
172 | return Text.rich(TextSpan(children: list));
173 | }
174 | return getTextWidget();
175 | }
176 |
177 | Widget getTextWidget() {
178 | return Text(
179 | text,
180 | overflow: textOverflow,
181 | textAlign: textAlign,
182 | maxLines: maxLines,
183 | style: style,
184 | );
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/lib/ui/page/view/banner/banner_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 |
3 | import '../../../../base/base_page_state.dart';
4 | import '../../../../utils/toast.dart';
5 | import '../../../widget/vip_banner.dart';
6 | import '../../../widget/vip_text.dart';
7 |
8 | ///AUTHOR:AbnerMing
9 | ///DATE:2023/5/22
10 | ///INTRODUCE:Banner页面
11 |
12 | class BannerPage extends StatefulWidget {
13 | const BannerPage({super.key});
14 |
15 | @override
16 | State createState() => _BannerPageState();
17 | }
18 |
19 | class _BannerPageState extends BasePageState {
20 | @override
21 | Widget getBody(BuildContext context) {
22 | // titleList: const ["Android干货铺全新改版!", "每日新闻,快速知道当天的时事!"],
23 | return SingleChildScrollView(
24 | child: Column(children: [
25 | VipBanner(
26 | imageList: const [
27 | "https://www.vipandroid.cn/ming/image/gan.png",
28 | "https://www.vipandroid.cn/ming/image/zao.png"
29 | ],
30 | bannerClick: (position) {
31 | //条目点击
32 | Toast.toast(context, msg: position.toString());
33 | }),
34 | const VipText("普通的轮播图"),
35 | VipBanner(
36 | imageList: const [
37 | "https://www.vipandroid.cn/ming/image/gan.png",
38 | "https://www.vipandroid.cn/ming/image/zao.png"
39 | ],
40 | indicatorType: IndicatorType.text,
41 | bannerClick: (position) {
42 | Toast.toast(context, msg: position.toString());
43 | }),
44 | const VipText("文字指示器"),
45 | VipBanner(
46 | imageList: const [
47 | "https://www.vipandroid.cn/ming/image/gan.png",
48 | "https://www.vipandroid.cn/ming/image/zao.png"
49 | ],
50 | indicatorType: IndicatorType.rectangle,
51 | indicatorRadius: 5,
52 | indicatorWidth: 20,
53 | indicatorHeight: 5,
54 | bannerClick: (position) {
55 | Toast.toast(context, msg: position.toString());
56 | }),
57 | const VipText("圆角指示器"),
58 | VipBanner(
59 | imageList: const [
60 | "https://www.vipandroid.cn/ming/image/gan.png",
61 | "https://www.vipandroid.cn/ming/image/zao.png"
62 | ],
63 | indicatorType: IndicatorType.rectangle,
64 | indicatorRadius: 5,
65 | indicatorWidth: 20,
66 | indicatorUnWidth: 10,
67 | indicatorHeight: 5,
68 | bannerClick: (position) {
69 | Toast.toast(context, msg: position.toString());
70 | }),
71 | const VipText("圆角指示器2"),
72 | VipBanner(
73 | imageList: const [
74 | "https://www.vipandroid.cn/ming/image/gan.png",
75 | "https://www.vipandroid.cn/ming/image/zao.png"
76 | ],
77 | indicatorType: IndicatorType.rectangle,
78 | bannerClick: (position) {
79 | Toast.toast(context, msg: position.toString());
80 | }),
81 | const VipText("矩形指示器"),
82 | VipBanner(
83 | imageList: const [
84 | "https://www.vipandroid.cn/ming/image/gan.png",
85 | "https://www.vipandroid.cn/ming/image/zao.png"
86 | ],
87 | indicatorType: IndicatorType.rectangle,
88 | indicatorWidth: 20,
89 | indicatorHeight: 5,
90 | bannerClick: (position) {
91 | Toast.toast(context, msg: position.toString());
92 | }),
93 | const VipText("矩形指示器2"),
94 | VipBanner(
95 | viewportFraction: 0.9,
96 | imageMarginLeft: 5,
97 | imageMarginRight: 5,
98 | imageList: const [
99 | "https://www.vipandroid.cn/ming/image/gan.png",
100 | "https://www.vipandroid.cn/ming/image/zao.png"
101 | ],
102 | bannerClick: (position) {
103 | Toast.toast(context, msg: position.toString());
104 | }),
105 | const VipText("伸缩"),
106 | VipBanner(
107 | viewportFraction: 0.8,
108 | bannerOtherScale: 0.9,
109 | radius: 10,
110 | imageList: const [
111 | "https://www.vipandroid.cn/ming/image/gan.png",
112 | "https://www.vipandroid.cn/ming/image/zao.png"
113 | ],
114 | bannerClick: (position) {
115 | Toast.toast(context, msg: position.toString());
116 | }),
117 | const VipText("伸缩2"),
118 | VipBanner(
119 | radius: 10,
120 | imageMarginLeft: 10,
121 | imageMarginRight: 10,
122 | imageList: const [
123 | "https://www.vipandroid.cn/ming/image/gan.png",
124 | "https://www.vipandroid.cn/ming/image/zao.png"
125 | ],
126 | bannerClick: (position) {
127 | Toast.toast(context, msg: position.toString());
128 | }),
129 | const VipText("圆角图片"),
130 | VipBanner(
131 | indicatorMarginLeft: 0,
132 | imageList: const [
133 | "https://www.vipandroid.cn/ming/image/gan.png",
134 | "https://www.vipandroid.cn/ming/image/zao.png"
135 | ],
136 | bannerClick: (position) {
137 | Toast.toast(context, msg: position.toString());
138 | }),
139 | const VipText("指示器左边"),
140 | VipBanner(
141 | indicatorMarginRight: 0,
142 | imageList: const [
143 | "https://www.vipandroid.cn/ming/image/gan.png",
144 | "https://www.vipandroid.cn/ming/image/zao.png"
145 | ],
146 | bannerClick: (position) {
147 | Toast.toast(context, msg: position.toString());
148 | }),
149 | const VipText("指示器右边"),
150 | VipBanner(
151 | indicatorBannerBottom: true,
152 | imageList: const [
153 | "https://www.vipandroid.cn/ming/image/gan.png",
154 | "https://www.vipandroid.cn/ming/image/zao.png"
155 | ],
156 | bannerClick: (position) {
157 | Toast.toast(context, msg: position.toString());
158 | }),
159 | const VipText("指示器图片下面")
160 | ]));
161 | }
162 |
163 | @override
164 | String getTitle() {
165 | return "Banner";
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/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: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "2.10.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: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "1.2.1"
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: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.17.0"
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_lints:
66 | dependency: "direct dev"
67 | description:
68 | name: flutter_lints
69 | sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
70 | url: "https://pub.dev"
71 | source: hosted
72 | version: "2.0.1"
73 | flutter_test:
74 | dependency: "direct dev"
75 | description: flutter
76 | source: sdk
77 | version: "0.0.0"
78 | intl:
79 | dependency: "direct main"
80 | description:
81 | name: intl
82 | sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
83 | url: "https://pub.dev"
84 | source: hosted
85 | version: "0.18.1"
86 | js:
87 | dependency: transitive
88 | description:
89 | name: js
90 | sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7"
91 | url: "https://pub.dev"
92 | source: hosted
93 | version: "0.6.5"
94 | lints:
95 | dependency: transitive
96 | description:
97 | name: lints
98 | sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593"
99 | url: "https://pub.dev"
100 | source: hosted
101 | version: "2.0.1"
102 | matcher:
103 | dependency: transitive
104 | description:
105 | name: matcher
106 | sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72"
107 | url: "https://pub.dev"
108 | source: hosted
109 | version: "0.12.13"
110 | material_color_utilities:
111 | dependency: transitive
112 | description:
113 | name: material_color_utilities
114 | sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
115 | url: "https://pub.dev"
116 | source: hosted
117 | version: "0.2.0"
118 | meta:
119 | dependency: transitive
120 | description:
121 | name: meta
122 | sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42"
123 | url: "https://pub.dev"
124 | source: hosted
125 | version: "1.8.0"
126 | path:
127 | dependency: transitive
128 | description:
129 | name: path
130 | sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b
131 | url: "https://pub.dev"
132 | source: hosted
133 | version: "1.8.2"
134 | sky_engine:
135 | dependency: transitive
136 | description: flutter
137 | source: sdk
138 | version: "0.0.99"
139 | source_span:
140 | dependency: transitive
141 | description:
142 | name: source_span
143 | sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
144 | url: "https://pub.dev"
145 | source: hosted
146 | version: "1.9.1"
147 | stack_trace:
148 | dependency: transitive
149 | description:
150 | name: stack_trace
151 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
152 | url: "https://pub.dev"
153 | source: hosted
154 | version: "1.11.0"
155 | stream_channel:
156 | dependency: transitive
157 | description:
158 | name: stream_channel
159 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
160 | url: "https://pub.dev"
161 | source: hosted
162 | version: "2.1.1"
163 | string_scanner:
164 | dependency: transitive
165 | description:
166 | name: string_scanner
167 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
168 | url: "https://pub.dev"
169 | source: hosted
170 | version: "1.2.0"
171 | term_glyph:
172 | dependency: transitive
173 | description:
174 | name: term_glyph
175 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
176 | url: "https://pub.dev"
177 | source: hosted
178 | version: "1.2.1"
179 | test_api:
180 | dependency: transitive
181 | description:
182 | name: test_api
183 | sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206
184 | url: "https://pub.dev"
185 | source: hosted
186 | version: "0.4.16"
187 | vector_math:
188 | dependency: transitive
189 | description:
190 | name: vector_math
191 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
192 | url: "https://pub.dev"
193 | source: hosted
194 | version: "2.1.4"
195 | sdks:
196 | dart: ">=2.19.6 <3.0.0"
197 |
--------------------------------------------------------------------------------
/lib/ui/page/view/text/text_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../../base/base_page_state.dart';
4 | import '../../../../constants/color_constant.dart';
5 | import '../../../../constants/dimen_constant.dart';
6 | import '../../../../constants/image_constant.dart';
7 | import '../../../../data/bean/text_rich_bean.dart';
8 | import '../../../../utils/hex_color.dart';
9 | import '../../../../utils/toast.dart';
10 | import '../../../widget/vip_text.dart';
11 |
12 | ///AUTHOR:AbnerMing
13 | ///DATE:2023/5/12
14 | ///INTRODUCE:Text组件效果页面
15 |
16 | class TextPage extends StatefulWidget {
17 | const TextPage({super.key});
18 |
19 | @override
20 | State createState() => _TextPageState();
21 | }
22 |
23 | class _TextPageState extends BasePageState {
24 | @override
25 | Widget getBody(BuildContext context) {
26 | return Column(
27 | children: [
28 | VipText("普通文字", marginTop: DimenConstant.dimen_10, onClick: () {
29 | Toast.toast(context, msg: "普通文字");
30 | }),
31 | VipText("加粗文字",
32 | marginTop: DimenConstant.dimen_10,
33 | style: const TextStyle(fontWeight: FontWeight.bold), onClick: () {
34 | Toast.toast(context, msg: "加粗文字");
35 | }),
36 | VipText("倾斜文字",
37 | marginTop: DimenConstant.dimen_10,
38 | style: const TextStyle(fontStyle: FontStyle.italic), onClick: () {
39 | Toast.toast(context, msg: "倾斜文字");
40 | }),
41 | VipText("文字背景颜色",
42 | marginTop: DimenConstant.dimen_10,
43 | backgroundColor: HexColor(ColorConstant.color_74d1d7), onClick: () {
44 | Toast.toast(context, msg: "文字背景颜色");
45 | }),
46 | VipText("文字圆角背景",
47 | marginTop: DimenConstant.dimen_10,
48 | radius: 10,
49 | solidColor: HexColor(ColorConstant.color_74d1d7),
50 | padding: 3, onClick: () {
51 | Toast.toast(context, msg: "文字圆角背景");
52 | }),
53 | VipText("文字圆角框背景",
54 | marginTop: DimenConstant.dimen_10,
55 | radius: 10,
56 | strokeColor: Colors.red,
57 | padding: 3, onClick: () {
58 | Toast.toast(context, msg: "文字圆角框背景");
59 | }),
60 | VipText("圆",
61 | marginTop: DimenConstant.dimen_10,
62 | isCircle: true,
63 | solidColor: Colors.cyan,
64 | padding: 8, onClick: () {
65 | Toast.toast(context, msg: "圆");
66 | }),
67 | VipText("下划线文字",
68 | marginTop: DimenConstant.dimen_10,
69 | style: const TextStyle(decoration: TextDecoration.underline),
70 | onClick: () {
71 | Toast.toast(context, msg: "下划线文字");
72 | }),
73 | VipText("下划波浪线文字",
74 | marginTop: DimenConstant.dimen_10,
75 | style: const TextStyle(
76 | decoration: TextDecoration.underline,
77 | decorationStyle: TextDecorationStyle.wavy,
78 | ), onClick: () {
79 | Toast.toast(context, msg: "下划波浪线文字");
80 | }),
81 | VipText("删除线文字",
82 | marginTop: DimenConstant.dimen_10,
83 | style: const TextStyle(decoration: TextDecoration.lineThrough),
84 | onClick: () {
85 | Toast.toast(context, msg: "删除线文字");
86 | }),
87 | ShaderMask(
88 | shaderCallback: (Rect bounds) {
89 | return const LinearGradient(
90 | colors: [Colors.red, Colors.blue],
91 | ).createShader(Offset.zero & bounds.size);
92 | },
93 | child: VipText(
94 | '文字设置渐变色',
95 | marginTop: DimenConstant.dimen_10,
96 | style: const TextStyle(
97 | fontSize: 16,
98 | color: Colors.white,
99 | fontWeight: FontWeight.bold,
100 | ),
101 | onClick: () {
102 | Toast.toast(context, msg: "文字设置渐变色");
103 | },
104 | ),
105 | ),
106 | VipText("改变颜色",
107 | style: TextStyle(color: HexColor(ColorConstant.color_74d1d7)),
108 | marginTop: DimenConstant.dimen_10, onClick: () {
109 | Toast.toast(context, msg: "改变颜色");
110 | }),
111 | VipText("左边带有Icon文字",
112 | marginTop: DimenConstant.dimen_10,
113 | leftIcon: ImageConstant.buttonLeftDownLoad,
114 | iconMarginRight: 10, onClick: () {
115 | Toast.toast(context, msg: "左边带有Icon文字");
116 | }),
117 | VipText("右边带有Icon文字",
118 | marginTop: DimenConstant.dimen_10,
119 | rightIcon: ImageConstant.buttonLeftDownLoad,
120 | iconMarginLeft: 10, onClick: () {
121 | Toast.toast(context, msg: "右边带有Icon文字");
122 | }),
123 | VipText("上边带有Icon文字",
124 | marginTop: DimenConstant.dimen_10,
125 | strokeColor: HexColor(ColorConstant.color_74d1d7),
126 | topIcon: ImageConstant.buttonLeftDownLoad, onClick: () {
127 | Toast.toast(context, msg: "上边带有Icon文字");
128 | }),
129 | VipText("下边带有Icon文字",
130 | strokeColor: HexColor(ColorConstant.color_74d1d7),
131 | marginTop: DimenConstant.dimen_10,
132 | bottomIcon: ImageConstant.buttonLeftDownLoad, onClick: () {
133 | Toast.toast(context, msg: "下边带有Icon文字");
134 | }),
135 | VipText(
136 | "超出的文字展示省略号,不妨我们就简单测试一下,看看是否能实现,再多打些文字看看吧,马上就够了,不着急哈,再输入一点",
137 | textOverflow: TextOverflow.ellipsis,
138 | marginLeft: DimenConstant.dimen_10,
139 | marginRight: DimenConstant.dimen_10,
140 | maxLines: 2,
141 | marginTop: DimenConstant.dimen_10,
142 | onClick: () {
143 | Toast.toast(context, msg: "超出文字点击");
144 | },
145 | ),
146 | VipText("富文本设置", richList: [
147 | TextRichBean(text: "我已经阅读并同意"),
148 | TextRichBean(
149 | text: "《用户服务协议》",
150 | textColor: HexColor(ColorConstant.color_74d1d7)),
151 | TextRichBean(text: "和"),
152 | TextRichBean(
153 | text: "《用户隐私政策》", textColor: HexColor(ColorConstant.color_74d1d7))
154 | ], onRichClick: (position, richBean) {
155 | //富文本点击事件
156 | Toast.toast(context, msg: "${richBean.text}");
157 | }, marginTop: DimenConstant.dimen_10)
158 | ],
159 | );
160 | }
161 |
162 | @override
163 | String getTitle() {
164 | return "Text";
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/lib/ui/page/view/button/button_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../../base/base_page_state.dart';
4 | import '../../../../constants/color_constant.dart';
5 | import '../../../../constants/dimen_constant.dart';
6 | import '../../../../constants/image_constant.dart';
7 | import '../../../../constants/string_constant.dart';
8 | import '../../../../utils/hex_color.dart';
9 | import '../../../../utils/toast.dart';
10 | import '../../../widget/dashed_border.dart';
11 | import '../../../widget/vip_button.dart';
12 |
13 | ///AUTHOR:AbnerMing
14 | ///DATE:2023/5/12
15 | ///INTRODUCE:Button组件效果页面
16 |
17 | class ButtonPage extends StatefulWidget {
18 | const ButtonPage({super.key});
19 |
20 | @override
21 | State createState() => _ButtonPageState();
22 | }
23 |
24 | class _ButtonPageState extends BasePageState {
25 | @override
26 | Widget getBody(BuildContext context) {
27 | return Container(
28 | padding: const EdgeInsets.only(left: 20, right: 20),
29 | child: Column(
30 | crossAxisAlignment: CrossAxisAlignment.center,
31 | children: [
32 | getVipButton(StringConstant.mainButton,
33 | solidColor: HexColor(ColorConstant.color_74d1d7),
34 | color: Colors.white),
35 | getVipButton(
36 | StringConstant.mainLoadingButton,
37 | solidColor: HexColor(ColorConstant.color_74d1d7),
38 | color: Colors.white,
39 | isLeftLoading: true,
40 | ),
41 | getVipButton(StringConstant.mainDisabledButton,
42 | solidColor: HexColor(ColorConstant.color_d4d4d4),
43 | color: Colors.white),
44 | getVipButton(StringConstant.secondaryButton,
45 | strokeColor: HexColor(ColorConstant.color_74d1d7),
46 | color: HexColor(ColorConstant.color_74d1d7)),
47 | getVipButton(
48 | StringConstant.secondaryLoadingButton,
49 | strokeColor: HexColor(ColorConstant.color_74d1d7),
50 | color: HexColor(ColorConstant.color_74d1d7),
51 | isLeftLoading: true,
52 | leftLoadingImage: ImageConstant.buttonSelectProgress,
53 | ),
54 | getVipButton(StringConstant.secondaryDisabledButton,
55 | strokeColor: HexColor(ColorConstant.color_d4d4d4),
56 | color: HexColor(ColorConstant.color_d4d4d4)),
57 | getVipButton(StringConstant.warningButton,
58 | strokeColor: HexColor(ColorConstant.color_f64749),
59 | color: HexColor(ColorConstant.color_f64749)),
60 | getVipButton(StringConstant.warningLoadingButton,
61 | strokeColor: HexColor(ColorConstant.color_f64749),
62 | color: HexColor(ColorConstant.color_f64749),
63 | isLeftLoading: true,
64 | leftLoadingImage: ImageConstant.buttonWarnProgress),
65 | getVipButton(StringConstant.warningDisabledButton,
66 | strokeColor: HexColor(ColorConstant.color_d4d4d4),
67 | color: HexColor(ColorConstant.color_d4d4d4)),
68 | Row(children: [
69 | Expanded(
70 | flex: 1,
71 | child: getVipButton(StringConstant.savePhotoButton,
72 | strokeColor: HexColor(ColorConstant.color_74d1d7),
73 | color: HexColor(ColorConstant.color_74d1d7),
74 | leftIcon: ImageConstant.buttonLeftDownLoad)),
75 | Expanded(
76 | flex: 1,
77 | child: getVipButton(StringConstant.sendInviteButton,
78 | solidColor: HexColor(ColorConstant.color_74d1d7),
79 | color: Colors.white,
80 | marginLeft: 10.0,
81 | leftIcon: ImageConstant.buttonLeftSendInvite))
82 | ]),
83 | VipButton(
84 | StringConstant.gradientButton,
85 | height: DimenConstant.dimen_44,
86 | radius: DimenConstant.dimen_44,
87 | solidColor: HexColor(ColorConstant.color_74d1d7),
88 | style: const TextStyle(color: Colors.white),
89 | marginTop: DimenConstant.dimen_10,
90 | gradient: 0,
91 | gradientColorList: [
92 | HexColor(ColorConstant.color_74d1d7),
93 | HexColor(ColorConstant.color_44b9c0)
94 | ],
95 | gradientColorStops: const [0, 1],
96 | onClick: () {
97 | Toast.toast(context, msg: StringConstant.gradientButton);
98 | },
99 | ),
100 | Container(
101 | margin: const EdgeInsets.only(top: DimenConstant.dimen_10),
102 | child: CustomPaint(
103 | painter: DashedBorder(),
104 | child: Container(
105 | height: DimenConstant.dimen_44,
106 | ),
107 | ))
108 | ],
109 | ));
110 | }
111 |
112 | Widget getLine() {
113 | return Flex(
114 | direction: Axis.horizontal,
115 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
116 | children: List.generate(10, (_) {
117 | return Container(
118 | width: 10,
119 | height: 1,
120 | margin: const EdgeInsets.only(top: DimenConstant.dimen_22),
121 | child: const DecoratedBox(
122 | decoration: BoxDecoration(color: Colors.red),
123 | ),
124 | );
125 | }),
126 | );
127 | }
128 |
129 | /*
130 | * 返回VipButton
131 | * */
132 | VipButton getVipButton(String text,
133 | {strokeColor,
134 | solidColor,
135 | color,
136 | isLeftLoading,
137 | leftLoadingImage,
138 | marginLeft,
139 | leftIcon}) {
140 | return VipButton(
141 | text,
142 | alignment: Alignment.center,
143 | height: DimenConstant.dimen_44,
144 | marginTop: DimenConstant.dimen_10,
145 | strokeColor: strokeColor,
146 | solidColor: solidColor,
147 | radius: DimenConstant.dimen_44,
148 | style: TextStyle(color: color),
149 | isLeftLoading: isLeftLoading,
150 | leftLoadingImage: leftLoadingImage,
151 | leftIcon: leftIcon,
152 | marginLeft: marginLeft,
153 | onClick: () {
154 | //点击事件
155 | Toast.toast(context, msg: text);
156 | },
157 | );
158 | }
159 |
160 | @override
161 | String getTitle() {
162 | return "Button";
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/lib/ui/page/util/toast/toast_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../../base/base_page_state.dart';
4 | import '../../../../constants/color_constant.dart';
5 | import '../../../../constants/dimen_constant.dart';
6 | import '../../../../constants/image_constant.dart';
7 | import '../../../../constants/toast_type.dart';
8 | import '../../../../utils/hex_color.dart';
9 | import '../../../../utils/toast.dart';
10 | import '../../../widget/vip_button.dart';
11 |
12 | ///AUTHOR:AbnerMing
13 | ///DATE:2023/5/20
14 | ///INTRODUCE:Toast页面
15 |
16 | class ToastPage extends StatefulWidget {
17 | const ToastPage({super.key});
18 |
19 | @override
20 | State createState() => _ToastPageState();
21 | }
22 |
23 | class _ToastPageState extends BasePageState {
24 | @override
25 | Widget getBody(BuildContext context) {
26 | return Column(children: [
27 | VipButton("Toast(普通)",
28 | marginTop: DimenConstant.dimen_10,
29 | height: 40,
30 | marginLeft: DimenConstant.dimen_22,
31 | marginRight: DimenConstant.dimen_22,
32 | style: const TextStyle(color: Colors.white),
33 | solidColor: HexColor(ColorConstant.color_74d1d7),
34 | radius: DimenConstant.dimen_10, onClick: () {
35 | Toast.toast(context, msg: "普通");
36 | }),
37 | VipButton("Toast(长文本)",
38 | marginTop: DimenConstant.dimen_10,
39 | height: 40,
40 | marginLeft: DimenConstant.dimen_22,
41 | marginRight: DimenConstant.dimen_22,
42 | style: const TextStyle(color: Colors.white),
43 | solidColor: HexColor(ColorConstant.color_74d1d7),
44 | radius: DimenConstant.dimen_10, onClick: () {
45 | Toast.toast(context, msg: "提示文案最好请控制在十六个字以内超出换行");
46 | }),
47 | VipButton("Toast(更改背景)",
48 | marginTop: DimenConstant.dimen_10,
49 | height: 40,
50 | marginLeft: DimenConstant.dimen_22,
51 | marginRight: DimenConstant.dimen_22,
52 | style: const TextStyle(color: Colors.white),
53 | solidColor: HexColor(ColorConstant.color_74d1d7),
54 | radius: DimenConstant.dimen_10, onClick: () {
55 | Toast.toast(context, msg: "更改背景", bgColor: Colors.cyan);
56 | }),
57 | VipButton("Toast(更改位置)",
58 | marginTop: DimenConstant.dimen_10,
59 | height: 40,
60 | marginLeft: DimenConstant.dimen_22,
61 | marginRight: DimenConstant.dimen_22,
62 | style: const TextStyle(color: Colors.white),
63 | solidColor: HexColor(ColorConstant.color_74d1d7),
64 | radius: DimenConstant.dimen_10, onClick: () {
65 | Toast.toast(context,
66 | msg: "Toast(更改位置)", position: ToastPosition.center);
67 | }),
68 | VipButton("Toast(更改时长)",
69 | marginTop: DimenConstant.dimen_10,
70 | height: 40,
71 | marginLeft: DimenConstant.dimen_22,
72 | marginRight: DimenConstant.dimen_22,
73 | style: const TextStyle(color: Colors.white),
74 | solidColor: HexColor(ColorConstant.color_74d1d7),
75 | radius: DimenConstant.dimen_10, onClick: () {
76 | Toast.toast(context, msg: "更改时长", showTime: 5000);
77 | }),
78 | VipButton("普通成功提示",
79 | marginTop: DimenConstant.dimen_10,
80 | height: 40,
81 | marginLeft: DimenConstant.dimen_22,
82 | marginRight: DimenConstant.dimen_22,
83 | style: const TextStyle(color: Colors.white),
84 | solidColor: HexColor(ColorConstant.color_74d1d7),
85 | radius: DimenConstant.dimen_10, onClick: () {
86 | Toast.toast(context, msg: "普通成功提示", toastPrompt: ToastPrompt.success);
87 | }),
88 | VipButton("设置宽高成功提示",
89 | marginTop: DimenConstant.dimen_10,
90 | height: 40,
91 | marginLeft: DimenConstant.dimen_22,
92 | marginRight: DimenConstant.dimen_22,
93 | style: const TextStyle(color: Colors.white),
94 | solidColor: HexColor(ColorConstant.color_74d1d7),
95 | radius: DimenConstant.dimen_10, onClick: () {
96 | Toast.toast(context,
97 | msg: "设置宽高成功提示",
98 | pdHorizontal: 10,
99 | toastWidth: 100,
100 | toastHeight: 100,
101 | toastPrompt: ToastPrompt.success);
102 | }),
103 | VipButton("错误提示",
104 | marginTop: DimenConstant.dimen_10,
105 | height: 40,
106 | marginLeft: DimenConstant.dimen_22,
107 | marginRight: DimenConstant.dimen_22,
108 | style: const TextStyle(color: Colors.white),
109 | solidColor: HexColor(ColorConstant.color_74d1d7),
110 | radius: DimenConstant.dimen_10, onClick: () {
111 | Toast.toast(context, msg: "错误提示", toastPrompt: ToastPrompt.error);
112 | }),
113 | VipButton("警示提示",
114 | marginTop: DimenConstant.dimen_10,
115 | height: 40,
116 | marginLeft: DimenConstant.dimen_22,
117 | marginRight: DimenConstant.dimen_22,
118 | style: const TextStyle(color: Colors.white),
119 | solidColor: HexColor(ColorConstant.color_74d1d7),
120 | radius: DimenConstant.dimen_10, onClick: () {
121 | Toast.toast(context, msg: "警示提示", toastPrompt: ToastPrompt.warn);
122 | }),
123 | VipButton("正在加载",
124 | marginTop: DimenConstant.dimen_10,
125 | height: 40,
126 | marginLeft: DimenConstant.dimen_22,
127 | marginRight: DimenConstant.dimen_22,
128 | style: const TextStyle(color: Colors.white),
129 | solidColor: HexColor(ColorConstant.color_74d1d7),
130 | radius: DimenConstant.dimen_10, onClick: () {
131 | Toast.toast(context, msg: "正在加载", toastPrompt: ToastPrompt.loading);
132 | }),
133 | VipButton("单独设置Icon(本地)",
134 | marginTop: DimenConstant.dimen_10,
135 | height: 40,
136 | marginLeft: DimenConstant.dimen_22,
137 | marginRight: DimenConstant.dimen_22,
138 | style: const TextStyle(color: Colors.white),
139 | solidColor: HexColor(ColorConstant.color_74d1d7),
140 | radius: DimenConstant.dimen_10, onClick: () {
141 | Toast.toast(context,
142 | msg: "单独设置Icon", iconPath: ImageConstant.buttonLeftDownLoad);
143 | }),
144 | VipButton("单独设置Icon(网络)",
145 | marginTop: DimenConstant.dimen_10,
146 | height: 40,
147 | marginLeft: DimenConstant.dimen_22,
148 | marginRight: DimenConstant.dimen_22,
149 | style: const TextStyle(color: Colors.white),
150 | solidColor: HexColor(ColorConstant.color_74d1d7),
151 | radius: DimenConstant.dimen_10, onClick: () {
152 | Toast.toast(context,
153 | msg: "单独设置Icon",
154 | iconPath: "https://www.vipandroid.cn/ming/image/gan.png");
155 | }),
156 | ]);
157 | }
158 |
159 | @override
160 | String getTitle() {
161 | return "Toast";
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/lib/utils/toast.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../constants/image_constant.dart';
4 | import '../constants/toast_type.dart';
5 | import '../ui/widget/vip_btn_rotate_animate.dart';
6 |
7 | ///AUTHOR:AbnerMing
8 | ///DATE:2023/5/12
9 | ///INTRODUCE:Toast提示
10 |
11 | class Toast {
12 | // toast靠它加到屏幕上
13 | static OverlayEntry? _overlayEntry;
14 |
15 | // toast是否正在showing
16 | static bool _showing = false;
17 |
18 | // 开启一个新toast的当前时间,用于对比是否已经展示了足够时间
19 | static var _startedTime = DateTime.now();
20 |
21 | // 提示内容
22 | static String _msg = "";
23 |
24 | // toast显示时间
25 | static int _showTime = 1000;
26 |
27 | // 背景颜色
28 | static var _bgColor = Colors.black;
29 |
30 | // 文本颜色
31 | static var _textColor = Colors.white;
32 |
33 | // 文字大小
34 | static var _textSize = 14.0;
35 |
36 | // 显示位置
37 | static var _toastPosition = ToastPosition.bottom;
38 |
39 | // 左右边距
40 | static var _pdHorizontal = 24.0;
41 |
42 | // 上下边距
43 | static var _pdVertical = 10.0;
44 |
45 | // icon地址,可以是http,本地
46 | static var _iconPath = "";
47 |
48 | //icon成功提示
49 | static var _iconPrompt = ToastPrompt.none;
50 |
51 | //icon的位置
52 | static ToastIconPosition _iconPosition = ToastIconPosition.top;
53 |
54 | //设置toast 宽
55 | static double? _toastWidth;
56 |
57 | //设置toast 高
58 | static double? _toastHeight;
59 |
60 | //设置toast icon 宽 适用于单独设置icon
61 | static double? _toastIconWidth;
62 |
63 | //设置toast icon 高 适用于单独设置icon
64 | static double? _toastIconHeight;
65 |
66 | static void toast(
67 | BuildContext context, {
68 | //显示的文本
69 | String msg = "",
70 | //显示的时间 单位毫秒
71 | int showTime = 2000,
72 | //显示的背景
73 | Color bgColor = Colors.black,
74 | //显示的文本颜色
75 | Color textColor = Colors.white,
76 | //显示的文字大小
77 | double textSize = 14.0,
78 | //显示的位置
79 | ToastPosition position = ToastPosition.bottom,
80 | //文字水平方向的内边距
81 | double pdHorizontal = 24.0,
82 | //文字垂直方向的内边距
83 | double pdVertical = 10.0,
84 | //icon地址
85 | String iconPath = "",
86 | ToastPrompt toastPrompt = ToastPrompt.none,
87 | //icon的显示的位置
88 | ToastIconPosition iconPosition = ToastIconPosition.top,
89 | //Toast 宽
90 | double? toastWidth,
91 | //Toast 高
92 | double? toastHeight,
93 | //Toast icon宽
94 | double? toastIconWidth = 20,
95 | //Toast icon高
96 | double? toastIconHeight = 20,
97 | }) async {
98 | _msg = msg;
99 | _startedTime = DateTime.now();
100 | _showTime = showTime;
101 | _bgColor = bgColor;
102 | _textColor = textColor;
103 | _textSize = textSize;
104 | _toastPosition = position;
105 | _pdHorizontal = pdHorizontal;
106 | _pdVertical = pdVertical;
107 | _iconPath = iconPath;
108 | _iconPrompt = toastPrompt;
109 | _iconPosition = iconPosition;
110 | _toastWidth = toastWidth;
111 | _toastHeight = toastHeight;
112 | _toastIconWidth = toastIconWidth;
113 | _toastIconHeight = toastIconHeight;
114 | //获取OverlayState
115 | OverlayState overlayState = Overlay.of(context);
116 | _showing = true;
117 | if (_overlayEntry == null) {
118 | //OverlayEntry负责构建布局
119 | //通过OverlayEntry将构建的布局插入到整个布局的最上层
120 | _overlayEntry = OverlayEntry(
121 | builder: (BuildContext context) => Positioned(
122 | //top值,可以改变这个值来改变toast在屏幕中的位置
123 | top: buildToastPosition(context),
124 | child: Container(
125 | alignment: Alignment.center,
126 | width: MediaQuery.of(context).size.width,
127 | child: Padding(
128 | padding: const EdgeInsets.symmetric(horizontal: 40.0),
129 | child: AnimatedOpacity(
130 | opacity: _showing ? 1.0 : 0.0, //目标透明度
131 | duration: _showing
132 | ? const Duration(milliseconds: 100)
133 | : const Duration(milliseconds: 400),
134 | child: _buildToastWidget(),
135 | ),
136 | )),
137 | ));
138 | //插入到整个布局的最上层
139 | overlayState.insert(_overlayEntry!);
140 | } else {
141 | //重新绘制UI,类似setState
142 | _overlayEntry?.markNeedsBuild();
143 | }
144 | // 等待时间
145 | await Future.delayed(Duration(milliseconds: _showTime));
146 | //2秒后 到底消失不消失
147 | if (DateTime.now().difference(_startedTime).inMilliseconds >= _showTime) {
148 | _showing = false;
149 | _overlayEntry?.markNeedsBuild();
150 | await Future.delayed(const Duration(milliseconds: 400));
151 | _overlayEntry?.remove();
152 | _overlayEntry = null;
153 | }
154 | }
155 |
156 | //toast绘制
157 | static _buildToastWidget() {
158 | return Container(
159 | width: _toastWidth,
160 | height: _toastHeight,
161 | decoration: BoxDecoration(
162 | color: _bgColor,
163 | borderRadius: const BorderRadius.all(Radius.circular(5))),
164 | padding: EdgeInsets.symmetric(
165 | horizontal: _pdHorizontal, vertical: _pdVertical),
166 | child: (_iconPrompt != ToastPrompt.none || _iconPath != "")
167 | ? getIconText()
168 | : getText());
169 | }
170 |
171 | static Widget getIconText() {
172 | Widget widget = Column(children: [getIcon(), getText()]);
173 | switch (_iconPosition) {
174 | case ToastIconPosition.left: //左
175 | widget = Row(
176 | mainAxisAlignment: MainAxisAlignment.center,
177 | children: [getIcon(), getText()]);
178 | break;
179 | case ToastIconPosition.top: //上
180 | widget = Column(
181 | mainAxisAlignment: MainAxisAlignment.center,
182 | children: [getIcon(), getText()]);
183 | break;
184 | case ToastIconPosition.right: //右
185 | widget = Row(
186 | mainAxisAlignment: MainAxisAlignment.center,
187 | children: [getText(), getIcon()]);
188 | break;
189 | case ToastIconPosition.bottom: //下
190 | widget = Column(
191 | mainAxisAlignment: MainAxisAlignment.center,
192 | children: [getText(), getIcon()]);
193 | break;
194 | }
195 | return widget;
196 | }
197 |
198 | static Widget getIcon() {
199 | if (_iconPath != "") {
200 | Widget image;
201 | //是图片
202 | if (_iconPath.contains("http")) {
203 | //包含
204 | image = Image.network(
205 | _iconPath,
206 | width: _toastIconWidth,
207 | height: _toastIconHeight,
208 | );
209 | } else {
210 | image = Image.asset(
211 | _iconPath,
212 | width: _toastIconWidth,
213 | height: _toastIconHeight,
214 | );
215 | }
216 | return image;
217 | }
218 |
219 | //单独加载loading
220 | if (_iconPrompt == ToastPrompt.loading) {
221 | return const VipBtnRotateAnimate(imagePath: ImageConstant.buttonProgress);
222 | }
223 | IconData icon = Icons.done;
224 | if (_iconPrompt == ToastPrompt.error) {
225 | icon = Icons.close;
226 | } else if (_iconPrompt == ToastPrompt.warn) {
227 | icon = Icons.priority_high_rounded;
228 | }
229 | return Container(
230 | decoration: BoxDecoration(
231 | shape: BoxShape.circle,
232 | border: Border.all(width: 1, color: Colors.white)),
233 | padding: const EdgeInsets.all(5),
234 | margin: const EdgeInsets.only(bottom: 8),
235 | child: Icon(
236 | icon,
237 | size: 14,
238 | color: Colors.white,
239 | ));
240 | }
241 |
242 | /*
243 | * 文本提示框
244 | * */
245 | static Widget getText() {
246 | return Text(
247 | _msg,
248 | textAlign: TextAlign.center,
249 | style: TextStyle(
250 | fontSize: _textSize,
251 | color: _textColor,
252 | decoration: TextDecoration.none),
253 | );
254 | }
255 |
256 | /*
257 | * 设置toast位置
258 | * */
259 | static buildToastPosition(context) {
260 | var height = MediaQuery.of(context).size.height;
261 | double backResult;
262 | if (_toastPosition == ToastPosition.top) {
263 | backResult = height / 6;
264 | } else if (_toastPosition == ToastPosition.center) {
265 | backResult = height / 2;
266 | } else {
267 | backResult = (height / 6) * 5;
268 | }
269 | return backResult;
270 | }
271 | }
272 |
--------------------------------------------------------------------------------
/lib/ui/widget/vip_video_controller.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'package:flutter/material.dart';
3 | import '../../utils/time_util.dart';
4 | import 'vip_image.dart';
5 | import 'vip_text.dart';
6 |
7 | ///AUTHOR:AbnerMing
8 | ///DATE:2023/6/26
9 | ///INTRODUCE:Video控制器
10 |
11 | class VipVideoController extends StatefulWidget {
12 | final double? height; //高度
13 | final double? progressHeight; //进度条高度
14 | final String? videoPlayIcon; //视频播放Icon
15 | final String? videoPauseIcon; //视频暂停Icon
16 | final String? videoFullScreenIcon; //视频全屏Icon
17 | final String? videoExitFullScreenIcon; //退出全屏Icon
18 | final TextStyle? textStyle; //文本样式
19 | final Color? backgroundColor; //背景颜色
20 | final Color? progressColor; //进度颜色
21 | final Color? thumbColor; //thumbColor
22 | final double thumbRadius; //thumb大小
23 | final double? playTimeMarginLeft; //播放时间距离左边的距离
24 | final double? playTimeMarginRight; //播放时间距离左边的距离
25 | final double? videoTimeMarginLeft; //视频时间距离左边的距离
26 | final double? videoTimeMarginRight; //视频时间距离左边的距离
27 | final int? totalTime; //总时长
28 | final int? changeTime; //改变时长
29 | final bool? isTimer; //是否需要定时
30 | final ValueChanged? onVideoPlayClick; //视频播放点击
31 | final ValueChanged? onVideoFullScreenClick; //视频全屏点击点击
32 | // 滑动回调
33 | final ValueChanged? onVideoChanged;
34 |
35 | final ValueChanged? onVideoChangeStart; //拖动开始
36 | final ValueChanged? onVideoChangeEnd; //拖动结束
37 |
38 | final bool? isPlayed; //播放控制状态,暂停还是开始
39 | final bool? isFullScreen; //是否是全屏
40 |
41 | const VipVideoController({
42 | super.key,
43 | this.height = 30, //默认高度
44 | this.progressHeight = 5, //进度条高度
45 | this.videoPlayIcon,
46 | this.videoPauseIcon,
47 | this.videoFullScreenIcon,
48 | this.videoExitFullScreenIcon,
49 | this.textStyle,
50 | this.backgroundColor = Colors.grey,
51 | this.progressColor = Colors.black,
52 | this.thumbColor = Colors.black,
53 | this.thumbRadius = 8,
54 | this.playTimeMarginLeft = 5,
55 | this.playTimeMarginRight = 0,
56 | this.videoTimeMarginLeft = 0,
57 | this.videoTimeMarginRight = 5,
58 | this.totalTime = 0,
59 | this.changeTime = 0,
60 | this.isTimer = false, //默认不开启定时
61 | this.onVideoPlayClick,
62 | this.onVideoFullScreenClick,
63 | this.onVideoChanged,
64 | this.onVideoChangeStart,
65 | this.onVideoChangeEnd,
66 | this.isPlayed = true,
67 | this.isFullScreen = false,
68 | });
69 |
70 | @override
71 | State createState() => _VipVideoControllerState();
72 | }
73 |
74 | class _VipVideoControllerState extends State {
75 | var _progress = 0;
76 | bool _isPlayed = true; //播放控制状态,暂停还是开始
77 | bool _isFullScreen = false; //是否是全屏
78 | VoidCallback? onPlayClick; //播放点击事件
79 | VoidCallback? onFullScreenClick; //全屏点击事件
80 |
81 | // 定时器实例
82 | Timer? _timer;
83 |
84 | @override
85 | void initState() {
86 | _progress = widget.changeTime!;
87 | _isPlayed = widget.isPlayed!;
88 | _isFullScreen = widget.isFullScreen!;
89 | onPlayClick = () {
90 | //监听点击事件
91 | setState(() {
92 | _isPlayed = !_isPlayed;
93 | if (widget.onVideoPlayClick != null) {
94 | widget.onVideoPlayClick!(_isPlayed); //设置播放状态
95 | }
96 | if (_isPlayed) {
97 | _startTimer();
98 | } else {
99 | _pauseTimer();
100 | }
101 | });
102 | };
103 | onFullScreenClick = () {
104 | setState(() {
105 | _isFullScreen = !_isFullScreen;
106 | //设置全屏状态
107 | if (widget.onVideoFullScreenClick != null) {
108 | widget.onVideoFullScreenClick!(_isFullScreen);
109 | }
110 | });
111 | };
112 |
113 | _startTimer();
114 |
115 | super.initState();
116 | }
117 |
118 | /*
119 | * 开启定时
120 | * */
121 | void _startTimer() {
122 | if (widget.isTimer! && _timer == null) {
123 | //开启定时,一秒执行一次
124 | _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
125 | if (_progress >= widget.totalTime!) {
126 | _pauseTimer();
127 | } else {
128 | setState(() {
129 | _progress += 1000;
130 | });
131 | widget.onVideoChanged!(_progress);
132 | }
133 | });
134 | }
135 | }
136 |
137 | /*
138 | * 暂停定时
139 | * */
140 | void _pauseTimer() {
141 | if (_timer != null) {
142 | _timer!.cancel(); //取消计时器
143 | _timer = null;
144 | }
145 | }
146 |
147 | @override
148 | void dispose() {
149 | _pauseTimer();
150 | super.dispose();
151 | }
152 |
153 | /*
154 | *获取播放时长
155 | * */
156 | Widget getPlayTime(String text) {
157 | return VipText(
158 | text,
159 | style: widget.textStyle,
160 | marginLeft: widget.playTimeMarginLeft,
161 | marginRight: widget.playTimeMarginRight,
162 | );
163 | }
164 |
165 | /*
166 | *获取总的播放时长
167 | * */
168 | Widget getVideoTime(String text) {
169 | return VipText(
170 | text,
171 | style: widget.textStyle,
172 | marginLeft: widget.videoTimeMarginLeft,
173 | marginRight: widget.videoTimeMarginRight,
174 | );
175 | }
176 |
177 | /*
178 | * 获取播放Icon
179 | * */
180 | Widget getPlayIcon() {
181 | if (widget.videoPlayIcon == null) {
182 | return InkWell(
183 | onTap: onPlayClick,
184 | child: Icon(_isPlayed ? Icons.pause : Icons.play_arrow),
185 | );
186 | } else {
187 | return VipImage(
188 | _isPlayed ? widget.videoPlayIcon : widget.videoPauseIcon,
189 | onClick: onPlayClick,
190 | );
191 | }
192 | }
193 |
194 | /*
195 | * 获取全屏Icon
196 | * */
197 | Widget getFullScreenIcon() {
198 | if (widget.videoFullScreenIcon == null) {
199 | return InkWell(
200 | onTap: onFullScreenClick,
201 | child: Icon(_isFullScreen ? Icons.fullscreen_exit : Icons.fullscreen),
202 | );
203 | } else {
204 | return VipImage(
205 | _isFullScreen
206 | ? widget.videoFullScreenIcon
207 | : widget.videoExitFullScreenIcon,
208 | onClick: onFullScreenClick,
209 | );
210 | }
211 | }
212 |
213 | /*
214 | * 时间戳转换时间
215 | * */
216 | String timeStampToStringDate(int time) {
217 | String format = time < 1000 * 60 * 60 ? TimeUtil.m_s : TimeUtil.h_m_s;
218 | return TimeUtil.getTimeStampToStringDate(time, format: format);
219 | }
220 |
221 | @override
222 | Widget build(BuildContext context) {
223 | return SizedBox(
224 | height: widget.height,
225 | child: Row(
226 | children: [
227 | getPlayIcon(), //开始和暂停
228 | getPlayTime(timeStampToStringDate(_progress)), //时间
229 | Expanded(child: getSliderTheme()), //进度
230 | getVideoTime(timeStampToStringDate(widget.totalTime!)), //时间
231 | getFullScreenIcon() //全屏
232 | ],
233 | ));
234 | }
235 |
236 | Widget getSliderTheme() {
237 | var divisions = widget.totalTime! / 1000;
238 | return SliderTheme(
239 | data: SliderThemeData(
240 | //高度
241 | trackHeight: widget.progressHeight,
242 | //去掉长按光晕
243 | overlayColor: Colors.transparent,
244 | //背景颜色
245 | inactiveTrackColor: widget.backgroundColor,
246 | activeTrackColor: widget.progressColor,
247 | thumbColor: widget.thumbColor,
248 | thumbShape:
249 | RoundSliderThumbShape(enabledThumbRadius: widget.thumbRadius)),
250 | child: Slider(
251 | min: 0,
252 | max: widget.totalTime!.toDouble(),
253 | value: _progress.toDouble(),
254 | divisions: divisions.toInt(),
255 | onChangeStart: (progress) {
256 | if (widget.onVideoChangeStart != null) {
257 | widget.onVideoChangeStart!(progress.toInt());
258 | }
259 | },
260 | onChangeEnd: (progress) {
261 | if (widget.onVideoChangeEnd != null) {
262 | widget.onVideoChangeEnd!(progress.toInt());
263 | }
264 | if (_isPlayed) {
265 | //播放状态下,如果定时,才会执行
266 | _startTimer();
267 | }
268 | },
269 | onChanged: (double value) {
270 | setState(() {
271 | _progress = value.toInt();
272 | });
273 | //回调当前进度
274 | if (widget.onVideoChanged != null) {
275 | widget.onVideoChanged!(_progress);
276 | }
277 | },
278 | ),
279 | );
280 | }
281 | }
282 |
--------------------------------------------------------------------------------
/lib/ui/widget/vip_banner.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'package:flutter/material.dart';
3 |
4 | import '../../constants/image_constant.dart';
5 | import 'vip_text.dart';
6 |
7 | ///AUTHOR:AbnerMing
8 | ///DATE:2023/5/22
9 | ///INTRODUCE:Banner组件
10 | ///自定义须知,手势滑动时,取消定时,手势抬起,重新定时
11 | class VipBanner extends StatefulWidget {
12 | final List? imageList; //图片地址
13 | final List? titleList; //标题集合
14 | final double? radius; //图片圆角
15 | final double? height; //图片高度
16 | final double? imageMarginLeft; //图片距离左边的距离
17 | final double? imageMarginTop; //图片距离上边的距离
18 | final double? imageMarginRight; //图片距离右边的距离
19 | final double? imageMarginBottom; //图片距离下边的距离
20 | final double? imageMargin; //图片距离左上右下的距离
21 | final double? marginLeft; //距离左边的距离
22 | final double? marginTop; //距离上边的距离
23 | final double? marginRight; //距离右边的距离
24 | final double? marginBottom; //距离下边的距离
25 | final double? margin; //距离左上右下边的距离
26 | final double? indicatorMarginBottom; //指示器距离底部的距离
27 | final double? indicatorMarginRight; //指示器距离右边的距离
28 | final double? indicatorMarginLeft; //指示器距离左边的距离
29 | final Color? indicatorSelectColor; //指示器选中的颜色
30 | final Color? indicatorUnSelectColor; //指示器未选中的颜色
31 | final double? indicatorWidth; //指示器宽
32 | final double? indicatorHeight; //指示器高
33 | final double? indicatorUnWidth; //指示器未选中宽
34 | final double? indicatorUnHeight; //指示器未选中高
35 | final double? indicatorMargin; //指示器边距
36 | final IndicatorType? indicatorType; //指示器类型
37 | final double? indicatorRadius; //指示器圆角度数
38 | final bool? indicatorBannerBottom; //指示器位置,是在banner上还是Banner下
39 | final Color? indicatorBottomColor; //指示器在Banner下的背景,默认是透明
40 | final double? indicatorBottomHeight; //指示器在Banner下的高度
41 | final double? indicatorBottomMarginLeft; //指示器在Banner下的 距离左边
42 | final double? indicatorBottomMarginRight; //指示器在Banner下的 距离右边
43 | final MainAxisAlignment indicatorBottomMainAxisAlignment; //指示器在Banner下的位置
44 | final int? delay; //多少时间轮播一次
45 | final bool? autoPlay; //是否自动轮播
46 | final bool? showIndicators; //是否展示指示器
47 | final Function(int)? bannerClick; //点击事件
48 | final double? viewportFraction; //banner缩进
49 | final Alignment? textIndicatorAlignment; //文字的位置
50 | final TextStyle? textIndicatorStyle; //文字样式
51 | final Color? textIndicatorBgColor; //文字指示器背景
52 | final double? textIndicatorPadding; //文字指示器内边距
53 | final double? textIndicatorPaddingLeft; //文字指示器内边距左
54 | final double? textIndicatorPaddingTop; //文字指示器内边距上
55 | final double? textIndicatorPaddingRight; //文字指示器内边距右
56 | final double? textIndicatorPaddingBottom; //文字指示器内边距下
57 | final Color? titleBgColor; //文字Title背景
58 | final double? titleHeight; //文字Title高度
59 | final Alignment? titleAlignment; //文字Title的位置
60 | final TextStyle? titleStyle; //文字Title样式
61 | final double? titleMarginBottom; //文字Title距离底部
62 | final double? bannerOtherScale; //除中间外的其他图片缩放比例
63 | final String? placeholderImage; //Banner 占位图
64 | final String? errorImage; //错误图
65 | final BoxFit? imageBoxFit; //图片伸缩模式
66 |
67 | const VipBanner(
68 | {super.key,
69 | required this.imageList,
70 | this.radius = 0,
71 | this.height = 150, //默认高度150
72 | this.marginLeft = 0,
73 | this.marginTop = 0,
74 | this.marginRight = 0,
75 | this.marginBottom = 0,
76 | this.margin,
77 | this.imageMarginLeft = 0,
78 | this.imageMarginTop = 0,
79 | this.imageMarginRight = 0,
80 | this.imageMarginBottom = 0,
81 | this.imageMargin,
82 | this.indicatorMarginBottom = 10, //默认指示器距离底部10
83 | this.indicatorMarginRight, //指示器距离右边
84 | this.indicatorMarginLeft, //指示器距离右边
85 | this.indicatorSelectColor = Colors.red,
86 | this.indicatorUnSelectColor = Colors.grey,
87 | this.indicatorWidth = 10, //指示器宽
88 | this.indicatorHeight = 10, //指示器高
89 | this.indicatorMargin = 5, //指示器边距
90 | this.indicatorType = IndicatorType.circle,
91 | this.indicatorRadius = 0, //指示器圆角度数
92 | this.indicatorUnWidth,
93 | this.indicatorUnHeight,
94 | this.indicatorBannerBottom = false,
95 | this.indicatorBottomColor = Colors.transparent,
96 | this.indicatorBottomHeight = 30,
97 | this.indicatorBottomMarginLeft = 0,
98 | this.indicatorBottomMarginRight = 0,
99 | this.indicatorBottomMainAxisAlignment = MainAxisAlignment.center,
100 | this.delay = 5, //默认是5秒轮询一次
101 | this.autoPlay = true, //默认是自动轮播
102 | this.showIndicators = true, //默认展示指示器
103 | this.bannerClick,
104 | this.viewportFraction = 1, //banner缩进
105 | this.textIndicatorAlignment = Alignment.center,
106 | this.textIndicatorStyle,
107 | this.textIndicatorPadding,
108 | this.textIndicatorPaddingLeft = 0,
109 | this.textIndicatorPaddingTop = 0,
110 | this.textIndicatorPaddingRight = 0,
111 | this.textIndicatorPaddingBottom = 0,
112 | this.textIndicatorBgColor = Colors.transparent,
113 | this.titleList,
114 | this.titleBgColor = Colors.transparent,
115 | this.titleHeight,
116 | this.titleAlignment = Alignment.centerLeft,
117 | this.titleStyle,
118 | this.titleMarginBottom = 0,
119 | this.bannerOtherScale = 1.0,
120 | this.placeholderImage,
121 | this.imageBoxFit = BoxFit.cover,
122 | this.errorImage});
123 |
124 | @override
125 | State createState() => _CarouselState();
126 | }
127 |
128 | class _CarouselState extends State with WidgetsBindingObserver {
129 | late PageController _controller;
130 | int _currentPage = 0;
131 | int _pagePosition = 0;
132 |
133 | // 定时器实例
134 | Timer? _timer;
135 |
136 | //计时器的运行状态
137 | bool _isRunning = false;
138 |
139 | //是否触发了点击
140 | bool _isClick = true;
141 |
142 | /*
143 | * 开启定时
144 | * */
145 | void _startTimer() {
146 | if (!_isRunning) {
147 | _isRunning = true;
148 | _timer = Timer.periodic(Duration(seconds: widget.delay!), (timer) {
149 | _controller.animateToPage(_pagePosition + 1,
150 | duration: const Duration(milliseconds: 800),
151 | curve: Curves.easeInOut);
152 | });
153 | }
154 | }
155 |
156 | /*
157 | * 暂停定时
158 | * */
159 | void _pauseTimer() {
160 | if (_isRunning) {
161 | _isRunning = false;
162 | _timer?.cancel(); //取消计时器
163 | }
164 | }
165 |
166 | @override
167 | void initState() {
168 | super.initState();
169 | _controller = PageController(viewportFraction: widget.viewportFraction!);
170 | // 添加监听
171 | WidgetsBinding.instance.addObserver(this);
172 | if (widget.autoPlay!) {
173 | _startTimer();
174 | }
175 | }
176 |
177 | /*
178 | * 感知生命周期变化
179 | * */
180 | @override
181 | void didChangeAppLifecycleState(AppLifecycleState state) {
182 | super.didChangeAppLifecycleState(state);
183 | if (state == AppLifecycleState.resumed) {
184 | _startTimer(); //页面可见,开启定时
185 | } else if (state == AppLifecycleState.paused) {
186 | _pauseTimer(); //页面不可见,关闭定时
187 | }
188 | }
189 |
190 | @override
191 | void dispose() {
192 | _controller.dispose();
193 | _timer?.cancel();
194 | super.dispose();
195 | }
196 |
197 | /*
198 | * 页面滑动监听
199 | * */
200 | void _onPageChanged(int index) {
201 | var position = index % widget.imageList!.length;
202 | setState(() {
203 | _currentPage = position;
204 | _pagePosition = index;
205 | });
206 | }
207 |
208 | @override
209 | Widget build(BuildContext context) {
210 | Widget bannerImage = Container(
211 | margin: widget.margin != null
212 | ? EdgeInsets.all(widget.margin!)
213 | : EdgeInsets.only(
214 | left: widget.marginLeft!,
215 | top: widget.marginTop!,
216 | right: widget.marginRight!,
217 | bottom: widget.marginBottom!),
218 | height: widget.height,
219 | child: Listener(
220 | onPointerDown: (event) {
221 | //手指按下,定时取消
222 | _pauseTimer();
223 | _isClick = true;
224 | },
225 | onPointerMove: (event) {
226 | _isClick = false;
227 | },
228 | onPointerUp: (event) {
229 | //手指抬起,定时开启
230 | _startTimer();
231 | //作为点击事件
232 | if (_isClick && widget.bannerClick != null) {
233 | widget.bannerClick!(_currentPage);
234 | }
235 | },
236 | child: PageView.builder(
237 | controller: _controller,
238 | onPageChanged: _onPageChanged,
239 | itemBuilder: (context, index) {
240 | double endScale = 1.0;
241 | var position = index % widget.imageList!.length;
242 | if (position == _currentPage) {
243 | endScale = 1.0;
244 | } else {
245 | endScale = widget.bannerOtherScale!;
246 | }
247 | String imageUrl = widget.imageList![position];
248 | return Transform.scale(
249 | scale: endScale,
250 | child: Container(
251 | margin: widget.imageMargin != null
252 | ? EdgeInsets.all(widget.imageMargin!)
253 | : EdgeInsets.only(
254 | left: widget.imageMarginLeft!,
255 | top: widget.imageMarginTop!,
256 | right: widget.imageMarginRight!,
257 | bottom: widget.imageMarginBottom!),
258 | child: ClipRRect(
259 | //设置图片圆角
260 | borderRadius: BorderRadius.circular(widget.radius!),
261 | child: getBannerImage(imageUrl))));
262 | })),
263 | );
264 | return !widget.indicatorBannerBottom!
265 | ? Stack(
266 | children: [
267 | bannerImage,
268 | if (widget.titleList != null) bannerTitle(),
269 | if (widget.showIndicators!) getBannerIndicators()
270 | ],
271 | )
272 | : Column(children: [
273 | bannerImage,
274 | if (widget.showIndicators!) getBannerBottomIndicators()
275 | ]);
276 | }
277 |
278 | /*
279 | * 获取轮播图
280 | * */
281 | Widget getBannerImage(imageUrl) {
282 | //不需要占位图
283 | if (widget.placeholderImage == null) {
284 | return Image.network(
285 | imageUrl,
286 | fit: widget.imageBoxFit,
287 | );
288 | } else {
289 | return FadeInImage(
290 | fit: widget.imageBoxFit,
291 | placeholderFit: widget.imageBoxFit,
292 | placeholder: getPlaceholder(),
293 | image: NetworkImage(imageUrl),
294 | placeholderErrorBuilder: (ctx, err, stackTrace) =>
295 | _imagePlaceholder(),
296 | imageErrorBuilder: (ctx, err, stackTrace) => _imageError());
297 | }
298 | }
299 |
300 | /*
301 | * 占位图错误组件
302 | * */
303 | Widget _imagePlaceholder() {
304 | return Image.asset(ImageConstant.imageDefault, fit: widget.imageBoxFit);
305 | }
306 |
307 | /*
308 | * 错误组件
309 | * */
310 | Widget _imageError() {
311 | var imagePath = ImageConstant.imageDefault;
312 | if (widget.errorImage != null) {
313 | imagePath = widget.errorImage!;
314 | }
315 | return Image.asset(imagePath, fit: widget.imageBoxFit);
316 | }
317 |
318 | /*
319 | * 占位图
320 | * */
321 | ImageProvider getPlaceholder() {
322 | return AssetImage(widget.placeholderImage!);
323 | }
324 |
325 | /*
326 | * 获取指示器
327 | * */
328 | Widget getBannerIndicators() {
329 | return Positioned(
330 | left: widget.indicatorMarginRight != null
331 | ? null
332 | : widget.indicatorMarginLeft ?? 0,
333 | right: widget.indicatorMarginLeft != null
334 | ? null
335 | : widget.indicatorMarginRight ?? 0,
336 | bottom: widget.indicatorMarginBottom!,
337 | child: _buildIndicators(MainAxisAlignment.center),
338 | );
339 | }
340 |
341 | /*
342 | * 获取指示器,底部
343 | * */
344 | Widget getBannerBottomIndicators() {
345 | return Container(
346 | height: widget.indicatorBottomHeight,
347 | color: widget.indicatorBottomColor,
348 | margin: EdgeInsets.only(
349 | left: widget.indicatorBottomMarginLeft!,
350 | right: widget.indicatorBottomMarginRight!),
351 | child: _buildIndicators(widget.indicatorBottomMainAxisAlignment),
352 | );
353 | }
354 |
355 | /*
356 | * 指示器
357 | * */
358 | Widget _buildIndicators(mainAxisAlignment) {
359 | if (widget.indicatorType == IndicatorType.text) {
360 | //文字
361 | return Container(
362 | alignment: widget.textIndicatorAlignment,
363 | child: VipText(
364 | "${_currentPage + 1}/${widget.imageList!.length}",
365 | style: widget.textIndicatorStyle,
366 | backgroundColor: widget.textIndicatorBgColor,
367 | padding: widget.textIndicatorPadding,
368 | paddingLeft: widget.textIndicatorPaddingLeft,
369 | paddingTop: widget.textIndicatorPaddingTop,
370 | paddingRight: widget.textIndicatorPaddingRight,
371 | paddingBottom: widget.textIndicatorPaddingBottom,
372 | ),
373 | );
374 | }
375 | return Row(
376 | mainAxisAlignment: mainAxisAlignment,
377 | children: List.generate(widget.imageList!.length, (index) {
378 | return Container(
379 | width: _currentPage == index
380 | ? widget.indicatorWidth
381 | : widget.indicatorUnWidth ?? widget.indicatorWidth,
382 | height: _currentPage == index
383 | ? widget.indicatorHeight
384 | : widget.indicatorUnHeight ?? widget.indicatorHeight,
385 | margin: EdgeInsets.symmetric(horizontal: widget.indicatorMargin!),
386 | decoration: BoxDecoration(
387 | shape: widget.indicatorType == IndicatorType.circle
388 | ? BoxShape.circle
389 | : BoxShape.rectangle,
390 | borderRadius: widget.indicatorType == IndicatorType.rectangle
391 | ? BorderRadius.all(Radius.circular(widget.indicatorRadius!))
392 | : null,
393 | color: _currentPage == index
394 | ? widget.indicatorSelectColor
395 | : widget.indicatorUnSelectColor,
396 | ),
397 | );
398 | }),
399 | );
400 | }
401 |
402 | /*
403 | * 返回Banner标题描述
404 | * */
405 | Widget bannerTitle() {
406 | return Positioned(
407 | bottom: widget.titleMarginBottom,
408 | left: 0,
409 | right: 0,
410 | child: VipText(
411 | widget.titleList![_currentPage],
412 | height: widget.titleHeight ?? widget.titleHeight,
413 | backgroundColor: widget.titleBgColor,
414 | alignment: widget.titleAlignment,
415 | style: widget.titleStyle,
416 | ),
417 | );
418 | }
419 | }
420 |
421 | ///指示器类型
422 | enum IndicatorType { circle, rectangle, text }
423 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 54;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
16 | /* End PBXBuildFile section */
17 |
18 | /* Begin PBXCopyFilesBuildPhase section */
19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
20 | isa = PBXCopyFilesBuildPhase;
21 | buildActionMask = 2147483647;
22 | dstPath = "";
23 | dstSubfolderSpec = 10;
24 | files = (
25 | );
26 | name = "Embed Frameworks";
27 | runOnlyForDeploymentPostprocessing = 0;
28 | };
29 | /* End PBXCopyFilesBuildPhase section */
30 |
31 | /* Begin PBXFileReference section */
32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
45 | /* End PBXFileReference section */
46 |
47 | /* Begin PBXFrameworksBuildPhase section */
48 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
49 | isa = PBXFrameworksBuildPhase;
50 | buildActionMask = 2147483647;
51 | files = (
52 | );
53 | runOnlyForDeploymentPostprocessing = 0;
54 | };
55 | /* End PBXFrameworksBuildPhase section */
56 |
57 | /* Begin PBXGroup section */
58 | 9740EEB11CF90186004384FC /* Flutter */ = {
59 | isa = PBXGroup;
60 | children = (
61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
65 | );
66 | name = Flutter;
67 | sourceTree = "";
68 | };
69 | 97C146E51CF9000F007C117D = {
70 | isa = PBXGroup;
71 | children = (
72 | 9740EEB11CF90186004384FC /* Flutter */,
73 | 97C146F01CF9000F007C117D /* Runner */,
74 | 97C146EF1CF9000F007C117D /* Products */,
75 | );
76 | sourceTree = "";
77 | };
78 | 97C146EF1CF9000F007C117D /* Products */ = {
79 | isa = PBXGroup;
80 | children = (
81 | 97C146EE1CF9000F007C117D /* Runner.app */,
82 | );
83 | name = Products;
84 | sourceTree = "";
85 | };
86 | 97C146F01CF9000F007C117D /* Runner */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
92 | 97C147021CF9000F007C117D /* Info.plist */,
93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
97 | );
98 | path = Runner;
99 | sourceTree = "";
100 | };
101 | /* End PBXGroup section */
102 |
103 | /* Begin PBXNativeTarget section */
104 | 97C146ED1CF9000F007C117D /* Runner */ = {
105 | isa = PBXNativeTarget;
106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
107 | buildPhases = (
108 | 9740EEB61CF901F6004384FC /* Run Script */,
109 | 97C146EA1CF9000F007C117D /* Sources */,
110 | 97C146EB1CF9000F007C117D /* Frameworks */,
111 | 97C146EC1CF9000F007C117D /* Resources */,
112 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
114 | );
115 | buildRules = (
116 | );
117 | dependencies = (
118 | );
119 | name = Runner;
120 | productName = Runner;
121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
122 | productType = "com.apple.product-type.application";
123 | };
124 | /* End PBXNativeTarget section */
125 |
126 | /* Begin PBXProject section */
127 | 97C146E61CF9000F007C117D /* Project object */ = {
128 | isa = PBXProject;
129 | attributes = {
130 | LastUpgradeCheck = 1300;
131 | ORGANIZATIONNAME = "";
132 | TargetAttributes = {
133 | 97C146ED1CF9000F007C117D = {
134 | CreatedOnToolsVersion = 7.3.1;
135 | LastSwiftMigration = 1100;
136 | };
137 | };
138 | };
139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
140 | compatibilityVersion = "Xcode 9.3";
141 | developmentRegion = en;
142 | hasScannedForEncodings = 0;
143 | knownRegions = (
144 | en,
145 | Base,
146 | );
147 | mainGroup = 97C146E51CF9000F007C117D;
148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
149 | projectDirPath = "";
150 | projectRoot = "";
151 | targets = (
152 | 97C146ED1CF9000F007C117D /* Runner */,
153 | );
154 | };
155 | /* End PBXProject section */
156 |
157 | /* Begin PBXResourcesBuildPhase section */
158 | 97C146EC1CF9000F007C117D /* Resources */ = {
159 | isa = PBXResourcesBuildPhase;
160 | buildActionMask = 2147483647;
161 | files = (
162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
166 | );
167 | runOnlyForDeploymentPostprocessing = 0;
168 | };
169 | /* End PBXResourcesBuildPhase section */
170 |
171 | /* Begin PBXShellScriptBuildPhase section */
172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
173 | isa = PBXShellScriptBuildPhase;
174 | alwaysOutOfDate = 1;
175 | buildActionMask = 2147483647;
176 | files = (
177 | );
178 | inputPaths = (
179 | );
180 | name = "Thin Binary";
181 | outputPaths = (
182 | );
183 | runOnlyForDeploymentPostprocessing = 0;
184 | shellPath = /bin/sh;
185 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
186 | };
187 | 9740EEB61CF901F6004384FC /* Run Script */ = {
188 | isa = PBXShellScriptBuildPhase;
189 | alwaysOutOfDate = 1;
190 | buildActionMask = 2147483647;
191 | files = (
192 | );
193 | inputPaths = (
194 | );
195 | name = "Run Script";
196 | outputPaths = (
197 | );
198 | runOnlyForDeploymentPostprocessing = 0;
199 | shellPath = /bin/sh;
200 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
201 | };
202 | /* End PBXShellScriptBuildPhase section */
203 |
204 | /* Begin PBXSourcesBuildPhase section */
205 | 97C146EA1CF9000F007C117D /* Sources */ = {
206 | isa = PBXSourcesBuildPhase;
207 | buildActionMask = 2147483647;
208 | files = (
209 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
210 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
211 | );
212 | runOnlyForDeploymentPostprocessing = 0;
213 | };
214 | /* End PBXSourcesBuildPhase section */
215 |
216 | /* Begin PBXVariantGroup section */
217 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
218 | isa = PBXVariantGroup;
219 | children = (
220 | 97C146FB1CF9000F007C117D /* Base */,
221 | );
222 | name = Main.storyboard;
223 | sourceTree = "";
224 | };
225 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
226 | isa = PBXVariantGroup;
227 | children = (
228 | 97C147001CF9000F007C117D /* Base */,
229 | );
230 | name = LaunchScreen.storyboard;
231 | sourceTree = "";
232 | };
233 | /* End PBXVariantGroup section */
234 |
235 | /* Begin XCBuildConfiguration section */
236 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
237 | isa = XCBuildConfiguration;
238 | buildSettings = {
239 | ALWAYS_SEARCH_USER_PATHS = NO;
240 | CLANG_ANALYZER_NONNULL = YES;
241 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
242 | CLANG_CXX_LIBRARY = "libc++";
243 | CLANG_ENABLE_MODULES = YES;
244 | CLANG_ENABLE_OBJC_ARC = YES;
245 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
246 | CLANG_WARN_BOOL_CONVERSION = YES;
247 | CLANG_WARN_COMMA = YES;
248 | CLANG_WARN_CONSTANT_CONVERSION = YES;
249 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
250 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
251 | CLANG_WARN_EMPTY_BODY = YES;
252 | CLANG_WARN_ENUM_CONVERSION = YES;
253 | CLANG_WARN_INFINITE_RECURSION = YES;
254 | CLANG_WARN_INT_CONVERSION = YES;
255 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
256 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
257 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
258 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
259 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
260 | CLANG_WARN_STRICT_PROTOTYPES = YES;
261 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
262 | CLANG_WARN_UNREACHABLE_CODE = YES;
263 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
264 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
265 | COPY_PHASE_STRIP = NO;
266 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
267 | ENABLE_NS_ASSERTIONS = NO;
268 | ENABLE_STRICT_OBJC_MSGSEND = YES;
269 | GCC_C_LANGUAGE_STANDARD = gnu99;
270 | GCC_NO_COMMON_BLOCKS = YES;
271 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
272 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
273 | GCC_WARN_UNDECLARED_SELECTOR = YES;
274 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
275 | GCC_WARN_UNUSED_FUNCTION = YES;
276 | GCC_WARN_UNUSED_VARIABLE = YES;
277 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
278 | MTL_ENABLE_DEBUG_INFO = NO;
279 | SDKROOT = iphoneos;
280 | SUPPORTED_PLATFORMS = iphoneos;
281 | TARGETED_DEVICE_FAMILY = "1,2";
282 | VALIDATE_PRODUCT = YES;
283 | };
284 | name = Profile;
285 | };
286 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
287 | isa = XCBuildConfiguration;
288 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
289 | buildSettings = {
290 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
291 | CLANG_ENABLE_MODULES = YES;
292 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
293 | ENABLE_BITCODE = NO;
294 | INFOPLIST_FILE = Runner/Info.plist;
295 | LD_RUNPATH_SEARCH_PATHS = (
296 | "$(inherited)",
297 | "@executable_path/Frameworks",
298 | );
299 | PRODUCT_BUNDLE_IDENTIFIER = com.vip.flutter.flutterWidget;
300 | PRODUCT_NAME = "$(TARGET_NAME)";
301 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
302 | SWIFT_VERSION = 5.0;
303 | VERSIONING_SYSTEM = "apple-generic";
304 | };
305 | name = Profile;
306 | };
307 | 97C147031CF9000F007C117D /* Debug */ = {
308 | isa = XCBuildConfiguration;
309 | buildSettings = {
310 | ALWAYS_SEARCH_USER_PATHS = NO;
311 | CLANG_ANALYZER_NONNULL = YES;
312 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
313 | CLANG_CXX_LIBRARY = "libc++";
314 | CLANG_ENABLE_MODULES = YES;
315 | CLANG_ENABLE_OBJC_ARC = YES;
316 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
317 | CLANG_WARN_BOOL_CONVERSION = YES;
318 | CLANG_WARN_COMMA = YES;
319 | CLANG_WARN_CONSTANT_CONVERSION = YES;
320 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
321 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
322 | CLANG_WARN_EMPTY_BODY = YES;
323 | CLANG_WARN_ENUM_CONVERSION = YES;
324 | CLANG_WARN_INFINITE_RECURSION = YES;
325 | CLANG_WARN_INT_CONVERSION = YES;
326 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
327 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
328 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
329 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
330 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
331 | CLANG_WARN_STRICT_PROTOTYPES = YES;
332 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
333 | CLANG_WARN_UNREACHABLE_CODE = YES;
334 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
335 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
336 | COPY_PHASE_STRIP = NO;
337 | DEBUG_INFORMATION_FORMAT = dwarf;
338 | ENABLE_STRICT_OBJC_MSGSEND = YES;
339 | ENABLE_TESTABILITY = YES;
340 | GCC_C_LANGUAGE_STANDARD = gnu99;
341 | GCC_DYNAMIC_NO_PIC = NO;
342 | GCC_NO_COMMON_BLOCKS = YES;
343 | GCC_OPTIMIZATION_LEVEL = 0;
344 | GCC_PREPROCESSOR_DEFINITIONS = (
345 | "DEBUG=1",
346 | "$(inherited)",
347 | );
348 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
349 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
350 | GCC_WARN_UNDECLARED_SELECTOR = YES;
351 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
352 | GCC_WARN_UNUSED_FUNCTION = YES;
353 | GCC_WARN_UNUSED_VARIABLE = YES;
354 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
355 | MTL_ENABLE_DEBUG_INFO = YES;
356 | ONLY_ACTIVE_ARCH = YES;
357 | SDKROOT = iphoneos;
358 | TARGETED_DEVICE_FAMILY = "1,2";
359 | };
360 | name = Debug;
361 | };
362 | 97C147041CF9000F007C117D /* Release */ = {
363 | isa = XCBuildConfiguration;
364 | buildSettings = {
365 | ALWAYS_SEARCH_USER_PATHS = NO;
366 | CLANG_ANALYZER_NONNULL = YES;
367 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
368 | CLANG_CXX_LIBRARY = "libc++";
369 | CLANG_ENABLE_MODULES = YES;
370 | CLANG_ENABLE_OBJC_ARC = YES;
371 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
372 | CLANG_WARN_BOOL_CONVERSION = YES;
373 | CLANG_WARN_COMMA = YES;
374 | CLANG_WARN_CONSTANT_CONVERSION = YES;
375 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
376 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
377 | CLANG_WARN_EMPTY_BODY = YES;
378 | CLANG_WARN_ENUM_CONVERSION = YES;
379 | CLANG_WARN_INFINITE_RECURSION = YES;
380 | CLANG_WARN_INT_CONVERSION = YES;
381 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
382 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
383 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
384 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
385 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
386 | CLANG_WARN_STRICT_PROTOTYPES = YES;
387 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
388 | CLANG_WARN_UNREACHABLE_CODE = YES;
389 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
390 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
391 | COPY_PHASE_STRIP = NO;
392 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
393 | ENABLE_NS_ASSERTIONS = NO;
394 | ENABLE_STRICT_OBJC_MSGSEND = YES;
395 | GCC_C_LANGUAGE_STANDARD = gnu99;
396 | GCC_NO_COMMON_BLOCKS = YES;
397 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
398 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
399 | GCC_WARN_UNDECLARED_SELECTOR = YES;
400 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
401 | GCC_WARN_UNUSED_FUNCTION = YES;
402 | GCC_WARN_UNUSED_VARIABLE = YES;
403 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
404 | MTL_ENABLE_DEBUG_INFO = NO;
405 | SDKROOT = iphoneos;
406 | SUPPORTED_PLATFORMS = iphoneos;
407 | SWIFT_COMPILATION_MODE = wholemodule;
408 | SWIFT_OPTIMIZATION_LEVEL = "-O";
409 | TARGETED_DEVICE_FAMILY = "1,2";
410 | VALIDATE_PRODUCT = YES;
411 | };
412 | name = Release;
413 | };
414 | 97C147061CF9000F007C117D /* Debug */ = {
415 | isa = XCBuildConfiguration;
416 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
417 | buildSettings = {
418 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
419 | CLANG_ENABLE_MODULES = YES;
420 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
421 | ENABLE_BITCODE = NO;
422 | INFOPLIST_FILE = Runner/Info.plist;
423 | LD_RUNPATH_SEARCH_PATHS = (
424 | "$(inherited)",
425 | "@executable_path/Frameworks",
426 | );
427 | PRODUCT_BUNDLE_IDENTIFIER = com.vip.flutter.flutterWidget;
428 | PRODUCT_NAME = "$(TARGET_NAME)";
429 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
430 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
431 | SWIFT_VERSION = 5.0;
432 | VERSIONING_SYSTEM = "apple-generic";
433 | };
434 | name = Debug;
435 | };
436 | 97C147071CF9000F007C117D /* Release */ = {
437 | isa = XCBuildConfiguration;
438 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
439 | buildSettings = {
440 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
441 | CLANG_ENABLE_MODULES = YES;
442 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
443 | ENABLE_BITCODE = NO;
444 | INFOPLIST_FILE = Runner/Info.plist;
445 | LD_RUNPATH_SEARCH_PATHS = (
446 | "$(inherited)",
447 | "@executable_path/Frameworks",
448 | );
449 | PRODUCT_BUNDLE_IDENTIFIER = com.vip.flutter.flutterWidget;
450 | PRODUCT_NAME = "$(TARGET_NAME)";
451 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
452 | SWIFT_VERSION = 5.0;
453 | VERSIONING_SYSTEM = "apple-generic";
454 | };
455 | name = Release;
456 | };
457 | /* End XCBuildConfiguration section */
458 |
459 | /* Begin XCConfigurationList section */
460 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
461 | isa = XCConfigurationList;
462 | buildConfigurations = (
463 | 97C147031CF9000F007C117D /* Debug */,
464 | 97C147041CF9000F007C117D /* Release */,
465 | 249021D3217E4FDB00AE95B9 /* Profile */,
466 | );
467 | defaultConfigurationIsVisible = 0;
468 | defaultConfigurationName = Release;
469 | };
470 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
471 | isa = XCConfigurationList;
472 | buildConfigurations = (
473 | 97C147061CF9000F007C117D /* Debug */,
474 | 97C147071CF9000F007C117D /* Release */,
475 | 249021D4217E4FDB00AE95B9 /* Profile */,
476 | );
477 | defaultConfigurationIsVisible = 0;
478 | defaultConfigurationName = Release;
479 | };
480 | /* End XCConfigurationList section */
481 | };
482 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
483 | }
484 |
--------------------------------------------------------------------------------