@ -0,0 +1,321 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### Java template
|
||||
# Compiled class file
|
||||
*.class
|
||||
# Log file
|
||||
*.log
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
medicine-server/src/main/resources/application.yml
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
### C++ template
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### C template
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
### Python template
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
**/.gradle
|
||||
**/node_modules
|
||||
|
||||
medicine-server/src/main/java/cn/xiaohaoo/admin/service/MedicinePredictionService.java
|
||||
medicine-server/src/main/resources/onnx/medicine_model.onnx
|
||||
|
@ -0,0 +1,46 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/Flutter/.last_build_id
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
|
||||
# Android Studio will place build artifacts here
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
@ -0,0 +1,3 @@
|
||||
# 中药识别APP端
|
||||
使用Flutter开发
|
||||
|
@ -0,0 +1,11 @@
|
||||
gradle-wrapper.jar
|
||||
/.gradle
|
||||
/captures/
|
||||
/gradlew
|
||||
/gradlew.bat
|
||||
/local.properties
|
||||
GeneratedPluginRegistrant.java
|
||||
|
||||
# Remember to never publicly share your keystore.
|
||||
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
|
||||
key.properties
|
@ -0,0 +1,59 @@
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||
if (flutterRoot == null) {
|
||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId 'cn.xiaohao.medicine'
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 30
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source '../..'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.xiaohao.flutter_demo01">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
@ -0,0 +1,44 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.xiaohao.flutter_demo01">
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<application
|
||||
android:label="中药识别"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<!-- Displays an Android View that continues showing the launch screen
|
||||
Drawable until Flutter paints its first frame, then this splash
|
||||
screen fades out. A splash screen is useful to avoid any visual
|
||||
gap between the end of Android's launch screen and the painting of
|
||||
Flutter's first frame. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||
android:resource="@drawable/launch_background"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
</manifest>
|
@ -0,0 +1,6 @@
|
||||
package com.xiaohao.flutter_demo01
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity: FlutterActivity() {
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
<item
|
||||
android:layout_height="200dp">
|
||||
<bitmap
|
||||
android:layout_height="wrap_content"
|
||||
android:height="200dp"
|
||||
android:src="@drawable/launcher" />
|
||||
</item>
|
||||
</layer-list>
|
After Width: | Height: | Size: 66 KiB |
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Modify this file to customize your launch splash screen -->
|
||||
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/white" />
|
||||
<item
|
||||
android:layout_height="500dp">
|
||||
<bitmap
|
||||
|
||||
android:src="@drawable/launcher" />
|
||||
</item>
|
||||
</layer-list>
|
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 13 KiB |
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.com.xiaohao.flutter_demo01">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
@ -0,0 +1,29 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.50'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.buildDir = '../build'
|
||||
subprojects {
|
||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||
project.evaluationDependsOn(':app')
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
@ -0,0 +1,6 @@
|
||||
#Fri Jun 23 08:50:38 CEST 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
|
@ -0,0 +1,11 @@
|
||||
include ':app'
|
||||
|
||||
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||
def properties = new Properties()
|
||||
|
||||
assert localPropertiesFile.exists()
|
||||
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
|
||||
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
@ -0,0 +1 @@
|
||||
include ':app'
|
@ -0,0 +1,199 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||
import 'package:medicine_app/request_util.dart';
|
||||
import 'package:medicine_app/search_page.dart';
|
||||
import 'package:medicine_app/slide_page_route.dart';
|
||||
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
|
||||
|
||||
import 'medicine_detail_page.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
HomePage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_HomePageState createState() => _HomePageState();
|
||||
}
|
||||
|
||||
class _HomePageState extends State<HomePage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).accentColor,
|
||||
elevation: 2,
|
||||
shadowColor: Theme.of(context).shadowColor,
|
||||
toolbarHeight: 50,
|
||||
title: TextButton(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.search_sharp,
|
||||
size: 17,
|
||||
),
|
||||
Text('搜索')
|
||||
],
|
||||
),
|
||||
style: ButtonStyle(
|
||||
foregroundColor: MaterialStateProperty.all(Colors.grey.shade500),
|
||||
shadowColor: MaterialStateProperty.all(Colors.transparent),
|
||||
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||
minimumSize: MaterialStateProperty.all(Size.fromHeight(32)),
|
||||
backgroundColor: MaterialStateProperty.all(Colors.white24.withOpacity(0.80)),
|
||||
textStyle: MaterialStateProperty.all(TextStyle(fontSize: 13, letterSpacing: 2)),
|
||||
shape: MaterialStateProperty.all(
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16.0)),
|
||||
),
|
||||
),
|
||||
onPressed: () => Navigator.of(context).push(SlidePageRoute(builder: SearchMedicinePage())),
|
||||
),
|
||||
),
|
||||
body: FutureBuilder(future: Future.sync(() {
|
||||
return Request.getDio().get('/medicine/outlines');
|
||||
}), builder: (BuildContext context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
Map data = json.decode(snapshot.data.toString());
|
||||
final List<dynamic> medicineList = data['data'];
|
||||
return ListView.builder(
|
||||
cacheExtent: 10,
|
||||
padding: EdgeInsets.only(bottom: 30),
|
||||
itemCount: medicineList.length + 1,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
if (index < medicineList.length) {
|
||||
final Map medicine = medicineList[index];
|
||||
final List<dynamic> medicineDetails = medicine['details'];
|
||||
return Card(
|
||||
color: Colors.grey[10],
|
||||
shadowColor: Colors.grey[50],
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
borderOnForeground: false,
|
||||
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 5),
|
||||
// 外边距
|
||||
child: Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 5),
|
||||
height: 250,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: ListTile(
|
||||
title: Text(
|
||||
"${medicine['name']}",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
)),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Builder(builder: (context) {
|
||||
final int pageCount = (medicineDetails.length / 6).ceil();
|
||||
final int maxPageCount = medicineDetails.length;
|
||||
final controller = PageController();
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: 3),
|
||||
child: Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
SmoothPageIndicator(
|
||||
controller: controller, // PageController
|
||||
count: pageCount,
|
||||
effect: WormEffect(dotWidth: 7, dotHeight: 7)),
|
||||
PageView(
|
||||
children: List.generate(pageCount, (index) {
|
||||
return GridView.count(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
crossAxisCount: 3,
|
||||
mainAxisSpacing: 20,
|
||||
crossAxisSpacing: 35,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 30,
|
||||
),
|
||||
children: medicineDetails
|
||||
.sublist(index * 6, min((index + 1) * 6, maxPageCount))
|
||||
.map((e) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(SlidePageRoute(
|
||||
builder: MedicineDetailPage(name: "${e['text']}"),
|
||||
));
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: null != e['icon']
|
||||
? CircleAvatar(
|
||||
backgroundColor:
|
||||
Theme.of(context).secondaryHeaderColor,
|
||||
backgroundImage: Image.network(
|
||||
e['icon'],
|
||||
fit: BoxFit.cover,
|
||||
loadingBuilder: (context, child, loadingProgress) {
|
||||
if (loadingProgress == null) {
|
||||
return child;
|
||||
} else {
|
||||
return Icon(Icons.downloading_outlined);
|
||||
}
|
||||
},
|
||||
).image,
|
||||
radius: 50,
|
||||
)
|
||||
: CircleAvatar(
|
||||
backgroundColor:
|
||||
Theme.of(context).secondaryHeaderColor,
|
||||
)),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text(
|
||||
'${e['text']}',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.black.withAlpha(170)),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList());
|
||||
}).toList(),
|
||||
controller: controller,
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'- 没有更多了 -',
|
||||
style: TextStyle(color: Colors.grey),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return Center(
|
||||
child: SpinKitFadingCircle(
|
||||
color: Theme.of(context).primaryColor,
|
||||
size: 32,
|
||||
));
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:medicine_app/mine_page.dart';
|
||||
import 'package:medicine_app/prediction_list_page.dart';
|
||||
import 'package:medicine_app/request_util.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:medicine_app/slide_page_route.dart';
|
||||
import 'home_page.dart';
|
||||
|
||||
void main() {
|
||||
Request.initialize();
|
||||
runApp(MedicineApp());
|
||||
if (Platform.isAndroid) {
|
||||
SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(statusBarColor: Colors.transparent);
|
||||
SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
|
||||
}
|
||||
}
|
||||
|
||||
class MedicineApp extends StatefulWidget {
|
||||
@override
|
||||
State<MedicineApp> createState() => _MedicineAppState();
|
||||
}
|
||||
|
||||
class _MedicineAppState extends State<MedicineApp> {
|
||||
final _pageList = [
|
||||
HomePage(),
|
||||
MinePage(),
|
||||
];
|
||||
|
||||
final _pageViewController = PageController();
|
||||
int _currentPageIndex = 0;
|
||||
|
||||
void _bottomAppBarTap(index) {
|
||||
setState(() {
|
||||
_currentPageIndex = index;
|
||||
});
|
||||
_pageViewController.animateToPage(index, duration: Duration(milliseconds: 300), curve: Curves.ease);
|
||||
}
|
||||
|
||||
Color _bottomAppBarItemColor(int index, BuildContext context) {
|
||||
return _currentPageIndex == index ? Theme.of(context).selectedRowColor : Theme.of(context).unselectedWidgetColor;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: '中药识别',
|
||||
home: Scaffold(
|
||||
body: PageView(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
children: _pageList,
|
||||
controller: _pageViewController,
|
||||
),
|
||||
floatingActionButton: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 48,
|
||||
height: 48,
|
||||
child: FloatingActionButton(
|
||||
backgroundColor: Theme.of(context).selectedRowColor,
|
||||
onPressed: () async {
|
||||
final ImagePicker _picker = ImagePicker();
|
||||
final XFile? picture =
|
||||
await _picker.pickImage(source: ImageSource.camera, maxHeight: 299, maxWidth: 299);
|
||||
if (picture != null) {
|
||||
Navigator.push(
|
||||
context,
|
||||
SlidePageRoute(
|
||||
builder: PredictionListPage(path: picture.path),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Fluttertoast.showToast(
|
||||
msg: "取消上传",
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
timeInSecForIosWeb: 1,
|
||||
backgroundColor: Theme.of(context).unselectedWidgetColor,
|
||||
textColor: Colors.white,
|
||||
fontSize: 13,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Icon(
|
||||
Icons.photo_camera,
|
||||
color: Colors.white70,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
|
||||
bottomNavigationBar: Container(
|
||||
height: 55,
|
||||
child: BottomAppBar(
|
||||
color: Colors.grey.shade50,
|
||||
elevation: 6,
|
||||
shape: CircularNotchedRectangle(),
|
||||
child: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: GestureDetector(
|
||||
onTap: () => _bottomAppBarTap(0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
_currentPageIndex == 0 ? Icons.home_rounded : Icons.home_outlined,
|
||||
color: _bottomAppBarItemColor(0, context),
|
||||
),
|
||||
Text(
|
||||
"首页",
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: _bottomAppBarItemColor(0, context)),
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
Expanded(flex: 1, child: Container()),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: GestureDetector(
|
||||
onTap: () => _bottomAppBarTap(1),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
_currentPageIndex == 1 ? Icons.person_rounded : Icons.person_outline_rounded,
|
||||
color: _bottomAppBarItemColor(1, context),
|
||||
),
|
||||
Text(
|
||||
"我的",
|
||||
style: TextStyle(
|
||||
fontSize: 10, fontWeight: FontWeight.w500, color: _bottomAppBarItemColor(1, context)),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
theme: Theme.of(context).copyWith(
|
||||
splashColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
primaryColor: Colors.deepOrange,
|
||||
primaryColorLight: Colors.white,
|
||||
primaryColorDark: Colors.black87,
|
||||
accentColor: Colors.deepOrange[400]?.withAlpha(224),
|
||||
selectedRowColor: Colors.deepOrangeAccent[700]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool isDebug() {
|
||||
return kDebugMode;
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||
import 'package:medicine_app/request_util.dart';
|
||||
|
||||
class MedicineDetailPage extends StatefulWidget {
|
||||
final String _name;
|
||||
|
||||
MedicineDetailPage({Key? key, required String name})
|
||||
: _name = name,
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
_MedicineDetailPageState createState() => _MedicineDetailPageState();
|
||||
}
|
||||
|
||||
class _MedicineDetailPageState extends State<MedicineDetailPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
color: Colors.black,
|
||||
icon: Icon(Icons.arrow_back_ios),
|
||||
iconSize: 18,
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
backgroundColor: Colors.white,
|
||||
centerTitle: true,
|
||||
elevation: 0.4,
|
||||
toolbarHeight: 55,
|
||||
title: Text(
|
||||
widget._name,
|
||||
style: TextStyle(color: Colors.black, fontSize: 16),
|
||||
),
|
||||
),
|
||||
body: FutureBuilder(
|
||||
future: Future.sync(() async {
|
||||
final response = await Request.getDio().get('/medicine/details', queryParameters: {'name': widget._name});
|
||||
return response.data['data'] as Map<String, dynamic>;
|
||||
}),
|
||||
builder: (BuildContext context, AsyncSnapshot<Map<String, dynamic>> snapshot) {
|
||||
final entries =
|
||||
snapshot.data?.entries.where((element) => element.key != '_id' && element.key != 'img').toList();
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
final maxLength = entries?.length ?? 0;
|
||||
return ListView.separated(
|
||||
padding: EdgeInsets.symmetric(vertical: 15, horizontal: 25),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
if (index == 0 && snapshot.data?['img'] != null) {
|
||||
return FractionallySizedBox(
|
||||
widthFactor: 0.9,
|
||||
child: ClipRRect(child: Image.network(snapshot.data?['img'])),
|
||||
);
|
||||
}
|
||||
if (index < maxLength) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.workspaces_filled,
|
||||
color: Theme.of(context).primaryColor,
|
||||
size: 12,
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 5),
|
||||
child: Text(
|
||||
entries?[index].key ?? '',
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 18),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
RichText(
|
||||
textAlign: TextAlign.left,
|
||||
text: TextSpan(
|
||||
text: entries?[index].value.toString() ?? '',
|
||||
style: TextStyle(color: Colors.black87, fontSize: 14, height: 3))),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Center(
|
||||
child: Text(
|
||||
'- 没有更多了 -',
|
||||
style: TextStyle(color: Colors.grey, height: 2.5),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
separatorBuilder: (_, int index) {
|
||||
if (index == 0 || index == maxLength - 1) {
|
||||
return SizedBox.shrink();
|
||||
} else {
|
||||
return Divider(
|
||||
height: 0.8, color: Theme.of(context).primaryColor.withAlpha(120));
|
||||
}
|
||||
},
|
||||
itemCount: maxLength + 1);
|
||||
} else {
|
||||
return Center(
|
||||
child: SpinKitFadingCircle(
|
||||
color: Theme.of(context).primaryColor,
|
||||
size: 35,
|
||||
));
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
||||
class MinePage extends StatefulWidget {
|
||||
MinePage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_MinePageState createState() => _MinePageState();
|
||||
}
|
||||
|
||||
class _MinePageState extends State<MinePage> {
|
||||
Container _menuListItem(String title, IconData iconData, Function? callback) {
|
||||
final _textStyle =
|
||||
TextStyle(color: Theme.of(context).primaryColorDark.withAlpha(160), fontSize: 15, fontWeight: FontWeight.w500);
|
||||
return Container(
|
||||
height: 55,
|
||||
child: GestureDetector(
|
||||
onTap: () => Fluttertoast.showToast(
|
||||
msg: "开发ing",
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
backgroundColor: Theme.of(context).unselectedWidgetColor,
|
||||
textColor: Colors.white,
|
||||
fontSize: 12,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Icon(
|
||||
iconData,
|
||||
size: 20,
|
||||
color: Theme.of(context).accentColor,
|
||||
)),
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: Text(
|
||||
title,
|
||||
style: _textStyle,
|
||||
)),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Icon(
|
||||
Icons.chevron_right,
|
||||
color: Colors.grey.shade400,
|
||||
size: 18,
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).accentColor,
|
||||
centerTitle: true,
|
||||
elevation: 0,
|
||||
toolbarHeight: 55,
|
||||
title: Text(
|
||||
"个人中心",
|
||||
style: TextStyle(color: Colors.black, fontSize: 16),
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 150,
|
||||
alignment: Alignment.center,
|
||||
color: Theme.of(context).accentColor,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.account_circle_sharp,
|
||||
size: 50,
|
||||
color: Colors.black87,
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => {},
|
||||
child: Text(
|
||||
'点击登录',
|
||||
style: TextStyle(color: Colors.black87),
|
||||
),
|
||||
style: ButtonStyle(overlayColor: MaterialStateProperty.all(Colors.transparent)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
ListView.separated(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return _menuListItem('历史记录', Icons.timelapse_outlined, null);
|
||||
case 1:
|
||||
return _menuListItem('中药问答', Icons.question_answer, null);
|
||||
|
||||
case 2:
|
||||
return _menuListItem('方剂智能推荐', Icons.account_tree_rounded, null);
|
||||
case 3:
|
||||
return _menuListItem('收藏', Icons.add_to_photos, null);
|
||||
case 4:
|
||||
return _menuListItem('关于', Icons.info, null);
|
||||
case 5:
|
||||
return _menuListItem('分享', Icons.share, null);
|
||||
default:
|
||||
return Container();
|
||||
}
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return Divider(
|
||||
indent: 10, endIndent: 10, height: 0.5, color: Theme.of(context).accentColor.withAlpha(80));
|
||||
},
|
||||
itemCount: 6),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||
import 'package:medicine_app/medicine_detail_page.dart';
|
||||
import 'package:medicine_app/request_util.dart';
|
||||
import 'package:medicine_app/slide_page_route.dart';
|
||||
|
||||
class PredictionListPage extends StatefulWidget {
|
||||
final String _path;
|
||||
|
||||
PredictionListPage({Key? key, required String path})
|
||||
: _path = path,
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
_PredictionListPageState createState() => _PredictionListPageState();
|
||||
}
|
||||
|
||||
class _PredictionListPageState extends State<PredictionListPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
color: Colors.black,
|
||||
icon: Icon(Icons.arrow_back_ios),
|
||||
iconSize: 18,
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
backgroundColor: Colors.white,
|
||||
centerTitle: true,
|
||||
elevation: 0.4,
|
||||
toolbarHeight: 55,
|
||||
title: Text(
|
||||
"预测",
|
||||
style: TextStyle(color: Colors.black, fontSize: 17),
|
||||
),
|
||||
),
|
||||
body: FutureBuilder(
|
||||
future: Future.sync(() async {
|
||||
final FormData formData = FormData.fromMap({"picture": await MultipartFile.fromFile(widget._path)});
|
||||
final response = await Request.getDio().post('/medicine/prediction', data: formData);
|
||||
return response.data['data'] as Map<String, dynamic>;
|
||||
}),
|
||||
builder: (BuildContext context, AsyncSnapshot<Map<String, dynamic>> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
final entries = snapshot.data?.entries.toList().sublist(0, 14);
|
||||
final maxLength = entries?.length ?? 0;
|
||||
return ListView.separated(
|
||||
padding: EdgeInsets.symmetric(vertical: 10),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
if (index < maxLength) {
|
||||
return InkWell(
|
||||
splashColor: Colors.grey.shade300,
|
||||
onTap: () {
|
||||
Navigator.of(context).push(SlidePageRoute(
|
||||
builder: MedicineDetailPage(name: "${entries?[index].key}"),
|
||||
));
|
||||
},
|
||||
child: Container(
|
||||
height: 65,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Icon(
|
||||
Icons.wallpaper_rounded,
|
||||
color: Theme.of(context).primaryColor,
|
||||
)),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Text(
|
||||
'${entries?[index].key}',
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
||||
)),
|
||||
Expanded(flex: 3, child: Text('置信度:${((entries?[index].value) * 100).toStringAsFixed(2)}%')),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Icon(
|
||||
Icons.chevron_right,
|
||||
size: 18,
|
||||
color: Theme.of(context).primaryColor,
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Center(
|
||||
child: Text(
|
||||
'- 没有更多了 -',
|
||||
style: TextStyle(color: Colors.grey, height: 2.5),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
itemCount: maxLength + 1,
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return Divider(
|
||||
indent: 25, endIndent: 20, height: 0.5, color: Theme.of(context).primaryColor.withAlpha(80));
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return Center(
|
||||
child: SpinKitFadingCircle(
|
||||
color: Theme.of(context).primaryColor,
|
||||
size: 35,
|
||||
));
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import 'main.dart';
|
||||
|
||||
class Request {
|
||||
static late final Dio _dio;
|
||||
|
||||
static Dio getDio() {
|
||||
return _dio;
|
||||
}
|
||||
|
||||
static void initialize() {
|
||||
final options;
|
||||
if (isDebug()) {
|
||||
options = BaseOptions(
|
||||
baseUrl: 'http://10.10.11.253:1433',
|
||||
// baseUrl: 'http://210.16.189.8:21433',
|
||||
connectTimeout: 5000,
|
||||
receiveTimeout: 3000,
|
||||
);
|
||||
} else {
|
||||
options = BaseOptions(
|
||||
baseUrl: 'http://210.16.189.8:21433',
|
||||
connectTimeout: 5000,
|
||||
receiveTimeout: 3000,
|
||||
);
|
||||
}
|
||||
_dio = Dio(options);
|
||||
}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||
import 'package:medicine_app/request_util.dart';
|
||||
import 'package:medicine_app/slide_page_route.dart';
|
||||
|
||||
import 'medicine_detail_page.dart';
|
||||
|
||||
class SearchMedicinePage extends StatefulWidget {
|
||||
SearchMedicinePage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_SearchMedicinePageState createState() => _SearchMedicinePageState();
|
||||
}
|
||||
|
||||
class _SearchMedicinePageState extends State<SearchMedicinePage> {
|
||||
Future? _responseFuture = Future.value();
|
||||
final _textEditingController = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
color: Colors.black,
|
||||
icon: Icon(Icons.arrow_back_ios),
|
||||
iconSize: 18,
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0.4,
|
||||
titleSpacing: 0,
|
||||
toolbarHeight: 55,
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 32,
|
||||
child: TextField(
|
||||
autofocus: false,
|
||||
controller: _textEditingController,
|
||||
style: TextStyle(fontSize: 15),
|
||||
cursorColor: Theme.of(context).primaryColor,
|
||||
decoration: InputDecoration(
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(Icons.close_rounded, size: 17),
|
||||
color: Theme.of(context).primaryColor,
|
||||
onPressed: () => _textEditingController.clear(),
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
focusColor: Theme.of(context).primaryColor,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).primaryColor,
|
||||
width: 1,
|
||||
)),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).primaryColor,
|
||||
width: 1,
|
||||
)),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).primaryColor,
|
||||
width: 1,
|
||||
)),
|
||||
prefixIcon: Icon(
|
||||
Icons.search_rounded,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
hintText: "请输入中药名",
|
||||
hintStyle: TextStyle(fontSize: 14, color: Colors.grey.shade500),
|
||||
),
|
||||
),
|
||||
),
|
||||
flex: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () async {
|
||||
var response = await Request.getDio()
|
||||
.get('/medicine/details', queryParameters: {'keyword': _textEditingController.text});
|
||||
var data = response.data['data'] as List<dynamic>;
|
||||
setState(() {
|
||||
_responseFuture = Future.value(data);
|
||||
});
|
||||
},
|
||||
child: Text('搜索'),
|
||||
style: ButtonStyle(
|
||||
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||
foregroundColor: MaterialStateProperty.all(Theme.of(context).primaryColor)),
|
||||
),
|
||||
flex: 1,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
body: FutureBuilder(
|
||||
future: _responseFuture ?? null,
|
||||
builder: (BuildContext context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
List? data = snapshot.data as List?;
|
||||
if (data == null || data.length == 0) {
|
||||
return Center(
|
||||
child: Text('暂无结果'),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
var item = data[index];
|
||||
var medicineItem =
|
||||
item.entries.where((element) => (element.key != 'name' && element.key != 'nameSource')).toList();
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(SlidePageRoute(
|
||||
builder: MedicineDetailPage(name: "${item['nameSource']}"),
|
||||
));
|
||||
},
|
||||
child: Html(
|
||||
data: '''<div>
|
||||
<h4>${item['nameSource']}</h4>
|
||||
<ul>
|
||||
${medicineItem.map((e) {
|
||||
return "<li><p>${e.key}</p><p>${e.value}</p></li>";
|
||||
}).join()}
|
||||
</ul>
|
||||
<!--You can pretty much put any html in here!-->
|
||||
</div>''',
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return Divider(
|
||||
indent: 10, endIndent: 10, height: 0.8, color: Theme.of(context).primaryColor.withAlpha(80));
|
||||
},
|
||||
itemCount: data.length,
|
||||
);
|
||||
} else {
|
||||
return SpinKitFadingCircle(
|
||||
color: Theme.of(context).primaryColor,
|
||||
size: 32,
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SlidePageRoute extends PageRouteBuilder {
|
||||
SlidePageRoute({required builder})
|
||||
: super(
|
||||
pageBuilder: (BuildContext context,
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,) {
|
||||
return builder;
|
||||
},
|
||||
transitionsBuilder: (BuildContext context,
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
Widget child,) {
|
||||
return SlideTransition(
|
||||
position: Tween<Offset>(
|
||||
begin: const Offset(1, 0),
|
||||
end: Offset.zero,
|
||||
).animate(animation),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
name: medicine_app
|
||||
description: 中药识别APP
|
||||
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `pub publish`. This is preferred for private packages.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
# The following defines the version and build number for your application.
|
||||
# A version number is three numbers separated by dots, like 1.2.43
|
||||
# followed by an optional build number separated by a +.
|
||||
# Both the version and the builder number may be overridden in flutter
|
||||
# build by specifying --build-name and --build-number, respectively.
|
||||
# In Android, build-name is used as versionName while build-number used as versionCode.
|
||||
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
|
||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
image_picker:
|
||||
dio:
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.2
|
||||
|
||||
smooth_page_indicator: ^0.3.0-nullsafety.0
|
||||
flutter_spinkit: ^5.0.0
|
||||
fluttertoast: ^8.0.7
|
||||
flutter_html: ^2.1.1
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter.
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||
|
||||
# For details regarding adding assets from package dependencies, see
|
||||
# https://flutter.dev/assets-and-images/#from-packages
|
||||
|
||||
# To add custom fonts to your application, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/custom-fonts/#from-packages
|
@ -0,0 +1,30 @@
|
||||
// This is a basic Flutter widget test.
|
||||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility that Flutter provides. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:medicine_app/main.dart';
|
||||
|
||||
|
||||
void main() {
|
||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(MedicineApp());
|
||||
|
||||
// Verify that our counter starts at 0.
|
||||
expect(find.text('0'), findsOneWidget);
|
||||
expect(find.text('1'), findsNothing);
|
||||
|
||||
// Tap the '+' icon and trigger a frame.
|
||||
await tester.tap(find.byIcon(Icons.add));
|
||||
await tester.pump();
|
||||
|
||||
// Verify that our counter has incremented.
|
||||
expect(find.text('0'), findsNothing);
|
||||
expect(find.text('1'), findsOneWidget);
|
||||
});
|
||||
}
|
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 9.7 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 15 KiB |