fix: code name space

This commit is contained in:
louis 2024-04-01 08:41:52 +08:00
parent d78a2598ac
commit 6cc8ec49c1
34 changed files with 2034 additions and 145 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.9 KiB

1
assets/icons/hr.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1711877484458" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="27084" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M281.258667 309.248c0 127.317333 103.082667 230.741333 230.741333 230.741333s230.741333-103.082667 230.741333-230.741333v-0.341333c0-127.317333-103.424-230.741333-230.741333-230.741334-127.317333 0.341333-230.741333 103.424-230.741333 231.082667z" fill="#06CC76" opacity=".6" p-id="27085"></path><path d="M462.165333 388.096l-261.12 375.466667c-26.624 38.570667 2.048 89.770667 49.834667 89.770666h522.24c47.786667 0 76.458667-51.2 49.834667-89.429333l-261.12-375.808c-23.552-34.133333-76.117333-34.133333-99.669334 0z" fill="#06CC76" opacity=".6" p-id="27086"></path><path d="M556.032 393.898667h-88.064c-19.114667 0-35.157333-15.701333-35.157333-35.157334 0-19.114667 15.701333-35.157333 35.157333-35.157333h88.064c19.456 0 35.157333 15.701333 35.157333 35.157333s-15.701333 35.157333-35.157333 35.157334z" fill="#FFFFFF" p-id="27087"></path><path d="M710.314667 730.112m-123.221334 0a123.221333 123.221333 0 1 0 246.442667 0 123.221333 123.221333 0 1 0-246.442667 0Z" fill="#06CC76" opacity=".6" p-id="27088"></path><path d="M645.12 784.042667c-8.192-9.216-14.336-20.138667-17.749333-32.085334l10.581333-13.994666c4.437333-5.802667 4.437333-14.336 0-20.138667l-9.557333-12.629333c4.096-12.629333 11.264-23.893333 20.821333-33.109334l15.36 1.706667c3.754667 0.341333 7.509333-0.341333 10.922667-2.048s5.802667-4.437333 7.509333-7.850667l6.144-13.653333c13.312-3.072 26.965333-3.413333 40.277333-0.682667l6.485334 14.336c1.365333 3.413333 4.096 6.144 7.509333 7.850667 3.413333 1.706667 7.168 2.730667 10.922667 2.048l17.408-1.706667c9.216 8.874667 16.042667 19.456 20.138666 31.402667l-11.264 14.677333c-4.437333 5.802667-4.437333 14.336 0 20.138667l12.288 16.042667c-3.413333 11.264-9.557333 21.504-17.408 30.378666l-21.162666-2.048c-3.754667-0.341333-7.509333 0.341333-10.922667 2.048s-5.802667 4.437333-7.509333 7.850667l-8.874667 19.114667c-11.946667 2.048-23.893333 2.048-35.498667-0.341334l-8.533333-18.773333c-1.706667-3.413333-4.096-6.144-7.509333-7.850667-3.413333-1.706667-7.168-2.730667-10.922667-2.048l-19.456 1.365334z m65.536-26.282667c15.701333 0 28.672-12.288 28.672-27.648s-12.970667-27.648-28.672-27.648-28.672 12.288-28.672 27.648 12.629333 27.648 28.672 27.648z" fill="#FFFFFF" p-id="27089"></path></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.9 KiB

1
assets/icons/product.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1711877400209" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="22626" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M306.5 756L512 584.7 215.1 401.4 12 564.3 306.5 756zM512 584.7L718.1 756 1012 564.3 808.9 401.4 512 584.7z m1.2 37.3v-1.2l-0.6 0.6-0.6-0.6v1.2L306.5 792.6l-88.3-57.7v64.9L512 975.9v0.6l0.6-0.6 0.6 0.6v-0.6l294.5-176.1v-64.9l-88.3 57.7L513.2 622zM12 239.2l203.1 162.2L512 218.8 306.5 47.5 12 239.2zM718.1 47.5L512 218.8l296.9 182.7L1012 239.2 718.1 47.5z" fill="#f4ea2a" p-id="22627"></path></svg>

After

Width:  |  Height:  |  Size: 728 B

1
assets/icons/report.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1711877635469" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="52624" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M0 0h1024v1024H0V0z" fill="#202425" opacity=".01" p-id="52625"></path><path d="M512 546.133333a34.133333 34.133333 0 0 1 28.808533 15.803734l238.933334 375.466666a34.133333 34.133333 0 0 1-57.617067 36.6592L512 643.857067l-210.1248 330.205866a34.133333 34.133333 0 0 1-57.617067-36.6592l238.933334-375.466666A34.133333 34.133333 0 0 1 512 546.133333z" fill="#11AA66" p-id="52626"></path><path d="M34.133333 170.666667a34.133333 34.133333 0 0 1 34.133334-34.133334h887.466666a34.133333 34.133333 0 0 1 34.133334 34.133334v614.4a34.133333 34.133333 0 0 1-34.133334 34.133333H68.266667a34.133333 34.133333 0 0 1-34.133334-34.133333V170.666667z" fill="#11AA66" p-id="52627"></path><path d="M809.198933 317.201067a34.133333 34.133333 0 0 1 0 48.264533l-238.933333 238.933333a34.133333 34.133333 0 0 1-48.264533 0L409.6 491.997867l-146.5344 146.5344a34.133333 34.133333 0 0 1-48.264533-48.264534l170.666666-170.666666a34.133333 34.133333 0 0 1 48.264534 0L546.133333 532.002133l214.801067-214.801066a34.133333 34.133333 0 0 1 48.264533 0z" fill="#FFFFFF" p-id="52628"></path><path d="M34.133333 68.266667a34.133333 34.133333 0 0 1 34.133334-34.133334h887.466666a34.133333 34.133333 0 1 1 0 68.266667H68.266667a34.133333 34.133333 0 0 1-34.133334-34.133333z" fill="#FFAA44" p-id="52629"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1711877604237" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="48599" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M190.01344 173.30176h642.85696c70.71744 0 128.57344 57.856 128.57344 128.57344V864.3584c0 45.0048-35.35872 80.36352-80.36352 80.36352H190.01344C119.296 944.73216 61.44 886.87616 61.44 816.15872V301.8752c0-70.71744 57.856-128.57344 128.57344-128.57344z" fill="#FFBB96" p-id="48600"></path><path d="M190.01344 173.30176h642.85696c70.71744 0 128.57344 57.856 128.57344 128.57344v401.78688l-128.57344 115.712-3.21536 3.21536-128.57344 122.14272h-511.0784C119.296 944.73216 61.44 886.87616 61.44 816.15872V301.8752c0-70.71744 57.856-128.57344 128.57344-128.57344z" fill="#CF5A1A" p-id="48601"></path><path d="M190.01344 173.30176h642.85696c70.71744 0 128.57344 57.856 128.57344 128.57344v401.78688H784.65024c-44.99456 0-80.35328 35.34848-80.35328 80.35328v160.7168H190.01344C119.296 944.73216 61.44 886.87616 61.44 816.15872V301.8752c0-70.71744 57.856-128.57344 128.57344-128.57344z" fill="#FF7A45" p-id="48602"></path><path d="M350.72 76.87168c19.29216 0 32.1536 12.86144 32.1536 32.14336v147.8656c0 19.27168-12.86144 32.13312-32.1536 32.13312-19.28192 0-32.13312-12.86144-32.13312-32.14336v-147.8656c0-19.27168 12.8512-32.13312 32.14336-32.13312z m321.4336 0c19.28192 0 32.14336 12.86144 32.14336 32.14336v147.8656c0 19.27168-12.86144 32.13312-32.14336 32.13312-19.28192 0-32.14336-12.86144-32.14336-32.14336v-147.8656c0-19.27168 12.86144-32.13312 32.1536-32.13312zM286.44352 494.72512a48.20992 48.20992 0 1 0 96.43008 0 48.20992 48.20992 0 0 0-96.43008 0zM511.4368 462.58176H704.3072c19.28192 0 32.14336 12.86144 32.14336 32.1536 0 19.28192-12.86144 32.13312-32.14336 32.13312H511.4368c-19.28192 0-32.14336-12.8512-32.14336-32.14336 0-19.28192 12.86144-32.14336 32.1536-32.14336z m0 160.7168H704.3072c19.28192 0 32.14336 12.86144 32.14336 32.1536 0 19.27168-12.86144 32.13312-32.14336 32.13312H511.4368c-19.28192 0-32.14336-12.86144-32.14336-32.14336 0-19.28192 12.86144-32.14336 32.1536-32.14336zM286.44352 655.44192a48.20992 48.20992 0 1 0 96.43008 0 48.20992 48.20992 0 0 0-96.43008 0z" fill="#FFBB96" p-id="48603"></path></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

1
assets/icons/vehicle.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1711877581105" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="46703" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M0 508.808533a509.064533 508.808533 0 1 0 1018.129067 0 509.064533 508.808533 0 1 0-1018.129067 0Z" fill="#BFA573" p-id="46704"></path><path d="M617.745067 341.333333c56.098133 0 118.528 51.421867 170.530133 106.581334 8.738133 9.2672 26.7776 16.384 54.101333 21.333333a34.133333 34.133333 0 0 1 27.938134 30.856533l0.119466 2.730667v89.634133c0 14.609067-11.229867 26.624-25.5488 27.835734l-2.389333 0.1024-44.561067 0.341333c-3.6864 34.1504-33.5872 60.347733-69.2224 59.477333-28.501333 0-54.135467-17.015467-64.7168-42.939733a67.140267 67.140267 0 0 1-4.2496-15.496533l-280.917333 2.082133c-3.9936 33.809067-33.7408 59.630933-69.137067 58.7776-28.501333 0-54.135467-17.015467-64.699733-42.939733a67.157333 67.157333 0 0 1-4.1472-14.779734l-18.722133 0.136534H221.866667a34.133333 34.133333 0 0 1-34.048-31.573334l-0.085334-2.56v-126.754133c0-61.4912 51.882667-120.234667 93.832534-122.760533L284.330667 341.333333h333.397333z m-160.119467 37.751467h-104.055467v85.333333h104.055467v-85.333333z m151.739733 0h-133.444266l-0.8192 85.333333h265.147733s-57.173333-85.333333-130.884267-85.333333z m-274.056533 0h-54.528c-20.616533 0-57.685333 40.277333-54.784 81.271467l0.375467 3.6352h16.3328c5.205333 0.546133 34.286933 0.5632 87.210666 0.0512l5.768534-0.0512-0.375467-84.906667z" fill="#FFFFFF" p-id="46705"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -93,7 +93,7 @@ final theme = ThemeData(
)),
appBarTheme: AppBarTheme(
centerTitle: true,
iconTheme: const IconThemeData(color: Colors.black),
iconTheme: const IconThemeData(color: Colors.white),
backgroundColor: AppTheme.primaryColor,
titleTextStyle: TextStyle(
color: Colors.white,

View File

@ -5,10 +5,10 @@ class GloablConfig {
// static const BASE_URL = "http://10.0.2.2:8001/api/";
// static const OSS_URL = "http://10.0.2.2:8001";
static const BASE_URL = "http://144.123.43.138:3001/api/";
static const OSS_URL = "http://144.123.43.138:3001";
// static const BASE_URL = "http://192.168.60.220:8001/api/";
// static const OSS_URL = "http://192.168.60.220:8001";
// static const BASE_URL = "http://144.123.43.138:3001/api/";
// static const OSS_URL = "http://144.123.43.138:3001";
static const BASE_URL = "http://192.168.60.220:8001/api/";
static const OSS_URL = "http://192.168.60.220:8001";
static const DOMAIN_NAME = "山矿通";
static const DEBUG = true;
static const PRIVACY_POLICY = 'http://h5.heeru.xyz/privacyPolicy.html';

View File

@ -1,6 +1,7 @@
import 'package:get/get.dart';
import 'package:sk_base_mobile/screens/inventory/inventory.dart';
import 'package:sk_base_mobile/screens/login/login.dart';
import 'package:sk_base_mobile/screens/sale_quotation/sale_quotation.dart';
import '../screens/landing/landing.dart';
import '../screens/mine/useinfo/userinfo.dart';
@ -10,10 +11,13 @@ class RouteConfig {
static const String login = '/login';
static const String userinfo = '/userinfo';
static const String inventory = '/inventory';
static const String saleQuotation = '/sale_quotation';
static final List<GetPage> getPages = [
GetPage(name: login, page: () => LoginScreen()),
GetPage(name: home, page: () => LandingPage()),
GetPage(name: userinfo, page: () => UserInfoPage()),
GetPage(name: inventory, page: () => const InventoryPage()),
GetPage(name: saleQuotation, page: () => const SaleQuotationPage())
];
}

View File

@ -0,0 +1,7 @@
class WorkBenchModel {
final String title;
final String icon;
final String route;
WorkBenchModel(
{required this.title, required this.route, required this.icon});
}

View File

@ -6,7 +6,7 @@ import 'package:sk_base_mobile/constants/constants.dart';
import 'package:sk_base_mobile/screens/inventory_inout/inventory_inout_controller.dart';
import 'package:sk_base_mobile/store/auth.store.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/widgets/core/zt_base_date_picker.dart';
import 'package:sk_base_mobile/widgets/core/sk_base_date_picker.dart';
class CustomAppBar extends StatelessWidget {
CustomAppBar({super.key});
@ -59,7 +59,7 @@ class CustomAppBar extends StatelessWidget {
showCupertinoModalPopup(
context: Get.overlayContext!,
builder: (BuildContext context) {
return ZtBaseDatePicker(
return SkBaseDatePicker(
initialDate: controller.endTime.value,
endDate: controller.endTime.value,
onDateTimeChanged: (date) {

View File

@ -14,6 +14,10 @@ class InvenotryInoutPage extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
SizedBox(
height: MediaQuery.of(context).padding.top,
),
UperBody(),
const Expanded(
child: TaskPageBody(),

View File

@ -16,9 +16,7 @@ class LandingPage extends StatelessWidget {
return Material(
child: Stack(children: [
const BackColors(),
SafeArea(
bottom: false,
child: Scaffold(
Scaffold(
floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked,
// floatingActionButton: [0].indexWhere(
@ -30,7 +28,7 @@ class LandingPage extends StatelessWidget {
bottomNavigationBar: BottomNavBar(),
backgroundColor: Colors.transparent,
body: Obx(() => controller.pages[controller.currentIndex.value]),
))
)
]),
);
}

View File

@ -124,7 +124,7 @@ class InventorySearch extends StatelessWidget {
// return Container(
// width: ScreenAdaper.width(200),
// constraints: BoxConstraints(minWidth: ScreenAdaper.width(200)),
// child: ZtSearchSelect<ProjectModel>(
// child: SkSearchSelect<ProjectModel>(
// isRequired: true,
// contentPadding:
// EdgeInsets.symmetric(horizontal: ScreenAdaper.width(15)),

View File

@ -13,10 +13,10 @@ import 'package:sk_base_mobile/screens/new_inventory_inout/components/inventory_
import 'package:sk_base_mobile/screens/new_inventory_inout/components/product_search.dart';
import 'package:sk_base_mobile/store/dict.store.dart';
import 'package:sk_base_mobile/util/util.dart';
import 'package:sk_base_mobile/widgets/core/zt_number_input.dart';
import 'package:sk_base_mobile/widgets/core/zt_search_select.dart';
import 'package:sk_base_mobile/widgets/core/zk_date_picker.dart';
import 'package:sk_base_mobile/widgets/core/zt_text_input.dart';
import 'package:sk_base_mobile/widgets/core/sk_number_input.dart';
import 'package:sk_base_mobile/widgets/core/sk_search_select.dart';
import 'package:sk_base_mobile/widgets/core/sk_date_picker.dart';
import 'package:sk_base_mobile/widgets/core/sk_text_input.dart';
import 'package:sk_base_mobile/widgets/gradient_button.dart';
import 'package:sk_base_mobile/screens/new_inventory_inout/new_inventory_inout_controller.dart';
@ -180,7 +180,7 @@ class NewInventoryInout extends StatelessWidget {
///
Widget buildProjectPicker() {
return ZtSearchSelect<ProjectModel>(
return SkSearchSelect<ProjectModel>(
isRequired: true,
textController: controller.projectTextController,
labelText: '项目',
@ -278,7 +278,7 @@ class NewInventoryInout extends StatelessWidget {
///
Widget buildDatePicker() {
return ZtDatePicker(
return SkDatePicker(
textController: controller.dateTextController,
onClear: () {
controller.payload.remove('time');
@ -294,7 +294,7 @@ class NewInventoryInout extends StatelessWidget {
///
Widget buildQuantity() {
return ZtNumberInput(
return SkNumberInput(
textController: controller.quantityTextController,
labelText: '数量',
onChanged: (value) {
@ -306,7 +306,7 @@ class NewInventoryInout extends StatelessWidget {
///
Widget buildUnitPrice() {
return ZtNumberInput(
return SkNumberInput(
textController: controller.unitPriceTextController,
labelText: '单价',
onChanged: (value) {
@ -317,7 +317,7 @@ class NewInventoryInout extends StatelessWidget {
///
Widget buildAmount() {
return ZtNumberInput(
return SkNumberInput(
textController: controller.amountTextController,
labelText: '金额',
onChanged: (value) {
@ -375,7 +375,7 @@ class NewInventoryInout extends StatelessWidget {
///
Widget buildRemark() {
return ZtTextInput(
return SkTextInput(
textController: controller.remarkTextController,
labelText: '备注',
isTextArea: true,

View File

@ -0,0 +1,692 @@
// ignore_for_file: avoid_print
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:data_table_2/data_table_2.dart';
// Copyright 2019 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The file was extracted from GitHub: https://github.com/flutter/gallery
// Changes and modifications by Maxim Saplin, 2021
/// Keeps track of selected rows, feed the data into DesertsDataSource
class RestorableDessertSelections extends RestorableProperty<Set<int>> {
Set<int> _dessertSelections = {};
/// Returns whether or not a dessert row is selected by index.
bool isSelected(int index) => _dessertSelections.contains(index);
/// Takes a list of [Dessert]s and saves the row indices of selected rows
/// into a [Set].
void setDessertSelections(List<Dessert> desserts) {
final updatedSet = <int>{};
for (var i = 0; i < desserts.length; i += 1) {
var dessert = desserts[i];
if (dessert.selected) {
updatedSet.add(i);
}
}
_dessertSelections = updatedSet;
notifyListeners();
}
@override
Set<int> createDefaultValue() => _dessertSelections;
@override
Set<int> fromPrimitives(Object? data) {
final selectedItemIndices = data as List<dynamic>;
_dessertSelections = {
...selectedItemIndices.map<int>((dynamic id) => id as int),
};
return _dessertSelections;
}
@override
void initWithValue(Set<int> value) {
_dessertSelections = value;
}
@override
Object toPrimitives() => _dessertSelections.toList();
}
int _idCounter = 0;
/// Domain model entity
class Dessert {
Dessert(
this.name,
this.calories,
this.fat,
this.carbs,
this.protein,
this.sodium,
this.calcium,
this.iron,
);
final int id = _idCounter++;
final String name;
final int calories;
final double fat;
final int carbs;
final double protein;
final int sodium;
final int calcium;
final int iron;
bool selected = false;
}
/// Data source implementing standard Flutter's DataTableSource abstract class
/// which is part of DataTable and PaginatedDataTable synchronous data fecthin API.
/// This class uses static collection of deserts as a data store, projects it into
/// DataRows, keeps track of selected items, provides sprting capability
class DessertDataSource extends DataTableSource {
DessertDataSource.empty(this.context) {
desserts = [];
}
DessertDataSource(this.context,
[sortedByCalories = false,
this.hasRowTaps = false,
this.hasRowHeightOverrides = false,
this.hasZebraStripes = false]) {
desserts = _desserts;
if (sortedByCalories) {
sort((d) => d.calories, true);
}
}
final BuildContext context;
late List<Dessert> desserts;
// Add row tap handlers and show snackbar
bool hasRowTaps = false;
// Override height values for certain rows
bool hasRowHeightOverrides = false;
// Color each Row by index's parity
bool hasZebraStripes = false;
void sort<T>(Comparable<T> Function(Dessert d) getField, bool ascending) {
desserts.sort((a, b) {
final aValue = getField(a);
final bValue = getField(b);
return ascending
? Comparable.compare(aValue, bValue)
: Comparable.compare(bValue, aValue);
});
notifyListeners();
}
void updateSelectedDesserts(RestorableDessertSelections selectedRows) {
_selectedCount = 0;
for (var i = 0; i < desserts.length; i += 1) {
var dessert = desserts[i];
if (selectedRows.isSelected(i)) {
dessert.selected = true;
_selectedCount += 1;
} else {
dessert.selected = false;
}
}
notifyListeners();
}
@override
DataRow2 getRow(int index, [Color? color]) {
final format = NumberFormat.decimalPercentPattern(
locale: 'en',
decimalDigits: 0,
);
assert(index >= 0);
if (index >= desserts.length) throw 'index > _desserts.length';
final dessert = desserts[index];
return DataRow2.byIndex(
index: index,
selected: dessert.selected,
color: color != null
? MaterialStateProperty.all(color)
: (hasZebraStripes && index.isEven
? MaterialStateProperty.all(Theme.of(context).highlightColor)
: null),
onSelectChanged: (value) {
if (dessert.selected != value) {
_selectedCount += value! ? 1 : -1;
assert(_selectedCount >= 0);
dessert.selected = value;
notifyListeners();
}
},
onTap: hasRowTaps
? () => _showSnackbar(context, 'Tapped on row ${dessert.name}')
: null,
onDoubleTap: hasRowTaps
? () => _showSnackbar(context, 'Double Tapped on row ${dessert.name}')
: null,
onLongPress: hasRowTaps
? () => _showSnackbar(context, 'Long pressed on row ${dessert.name}')
: null,
onSecondaryTap: hasRowTaps
? () => _showSnackbar(context, 'Right clicked on row ${dessert.name}')
: null,
onSecondaryTapDown: hasRowTaps
? (d) =>
_showSnackbar(context, 'Right button down on row ${dessert.name}')
: null,
specificRowHeight:
hasRowHeightOverrides && dessert.fat >= 25 ? 100 : null,
cells: [
DataCell(Text(dessert.name)),
DataCell(Text('${dessert.calories}'),
onTap: () => _showSnackbar(context,
'Tapped on a cell with "${dessert.calories}"', Colors.red)),
DataCell(Text(dessert.fat.toStringAsFixed(1))),
DataCell(Text('${dessert.carbs}')),
DataCell(Text(dessert.protein.toStringAsFixed(1))),
DataCell(Text('${dessert.sodium}')),
DataCell(Text(format.format(dessert.calcium / 100))),
DataCell(Text(format.format(dessert.iron / 100))),
],
);
}
@override
int get rowCount => desserts.length;
@override
bool get isRowCountApproximate => false;
@override
int get selectedRowCount => _selectedCount;
void selectAll(bool? checked) {
for (final dessert in desserts) {
dessert.selected = checked ?? false;
}
_selectedCount = (checked ?? false) ? desserts.length : 0;
notifyListeners();
}
}
/// Async datasource for AsynPaginatedDataTabke2 example. Based on AsyncDataTableSource which
/// is an extension to Flutter's DataTableSource and aimed at solving
/// saync data fetching scenarious by paginated table (such as using Web API)
class DessertDataSourceAsync extends AsyncDataTableSource {
DessertDataSourceAsync() {
print('DessertDataSourceAsync created');
}
DessertDataSourceAsync.empty() {
_empty = true;
print('DessertDataSourceAsync.empty created');
}
DessertDataSourceAsync.error() {
_errorCounter = 0;
print('DessertDataSourceAsync.error created');
}
bool _empty = false;
int? _errorCounter;
RangeValues? _caloriesFilter;
RangeValues? get caloriesFilter => _caloriesFilter;
set caloriesFilter(RangeValues? calories) {
_caloriesFilter = calories;
refreshDatasource();
}
final DesertsFakeWebService _repo = DesertsFakeWebService();
String _sortColumn = "name";
bool _sortAscending = true;
void sort(String columnName, bool ascending) {
_sortColumn = columnName;
_sortAscending = ascending;
refreshDatasource();
}
Future<int> getTotalRecords() {
return Future<int>.delayed(
const Duration(milliseconds: 0), () => _empty ? 0 : _dessertsX3.length);
}
@override
Future<AsyncRowsResponse> getRows(int startIndex, int count) async {
print('getRows($startIndex, $count)');
if (_errorCounter != null) {
_errorCounter = _errorCounter! + 1;
if (_errorCounter! % 2 == 1) {
await Future.delayed(const Duration(milliseconds: 1000));
throw 'Error #${((_errorCounter! - 1) / 2).round() + 1} has occured';
}
}
final format = NumberFormat.decimalPercentPattern(
locale: 'en',
decimalDigits: 0,
);
assert(startIndex >= 0);
// List returned will be empty is there're fewer items than startingAt
var x = _empty
? await Future.delayed(const Duration(milliseconds: 2000),
() => DesertsFakeWebServiceResponse(0, []))
: await _repo.getData(
startIndex, count, _caloriesFilter, _sortColumn, _sortAscending);
var r = AsyncRowsResponse(
x.totalRecords,
x.data.map((dessert) {
return DataRow(
key: ValueKey<int>(dessert.id),
//selected: dessert.selected,
onSelectChanged: (value) {
if (value != null) {
setRowSelection(ValueKey<int>(dessert.id), value);
}
},
cells: [
DataCell(Text(dessert.name)),
DataCell(Text('${dessert.calories}')),
DataCell(Text(dessert.fat.toStringAsFixed(1))),
DataCell(Text('${dessert.carbs}')),
DataCell(Text(dessert.protein.toStringAsFixed(1))),
DataCell(Text('${dessert.sodium}')),
DataCell(Text(format.format(dessert.calcium / 100))),
DataCell(Text(format.format(dessert.iron / 100))),
],
);
}).toList());
return r;
}
}
class DesertsFakeWebServiceResponse {
DesertsFakeWebServiceResponse(this.totalRecords, this.data);
/// THe total ammount of records on the server, e.g. 100
final int totalRecords;
/// One page, e.g. 10 reocrds
final List<Dessert> data;
}
class DesertsFakeWebService {
int Function(Dessert, Dessert)? _getComparisonFunction(
String column, bool ascending) {
var coef = ascending ? 1 : -1;
switch (column) {
case 'name':
return (Dessert d1, Dessert d2) => coef * d1.name.compareTo(d2.name);
case 'calories':
return (Dessert d1, Dessert d2) => coef * (d1.calories - d2.calories);
case 'fat':
return (Dessert d1, Dessert d2) => coef * (d1.fat - d2.fat).round();
case 'carbs':
return (Dessert d1, Dessert d2) => coef * (d1.carbs - d2.carbs);
case 'protein':
return (Dessert d1, Dessert d2) =>
coef * (d1.protein - d2.protein).round();
case 'sodium':
return (Dessert d1, Dessert d2) => coef * (d1.sodium - d2.sodium);
case 'calcium':
return (Dessert d1, Dessert d2) => coef * (d1.calcium - d2.calcium);
case 'iron':
return (Dessert d1, Dessert d2) => coef * (d1.iron - d2.iron);
}
return null;
}
Future<DesertsFakeWebServiceResponse> getData(int startingAt, int count,
RangeValues? caloriesFilter, String sortedBy, bool sortedAsc) async {
return Future.delayed(
Duration(
milliseconds: startingAt == 0
? 2650
: startingAt < 20
? 2000
: 400), () {
var result = _dessertsX3;
if (caloriesFilter != null) {
result = result
.where((e) =>
e.calories >= caloriesFilter.start &&
e.calories <= caloriesFilter.end)
.toList();
}
result.sort(_getComparisonFunction(sortedBy, sortedAsc));
return DesertsFakeWebServiceResponse(
result.length, result.skip(startingAt).take(count).toList());
});
}
}
int _selectedCount = 0;
List<Dessert> _desserts = <Dessert>[
Dessert(
'Frozen Yogurt',
159,
6.0,
24,
4.0,
87,
14,
1,
),
Dessert(
'Ice Cream Sandwich',
237,
9.0,
37,
4.3,
129,
8,
1,
),
Dessert(
'Eclair',
262,
16.0,
24,
6.0,
337,
6,
7,
),
Dessert(
'Cupcake',
305,
3.7,
67,
4.3,
413,
3,
8,
),
Dessert(
'Gingerbread',
356,
16.0,
49,
3.9,
327,
7,
16,
),
Dessert(
'Jelly Bean',
375,
0.0,
94,
0.0,
50,
0,
0,
),
Dessert(
'Lollipop',
392,
0.2,
98,
0.0,
38,
0,
2,
),
Dessert(
'Honeycomb',
408,
3.2,
87,
6.5,
562,
0,
45,
),
Dessert(
'Donut',
452,
25.0,
51,
4.9,
326,
2,
22,
),
Dessert(
'Apple Pie',
518,
26.0,
65,
7.0,
54,
12,
6,
),
Dessert(
'Frozen Yougurt with sugar',
168,
6.0,
26,
4.0,
87,
14,
1,
),
Dessert(
'Ice Cream Sandwich with sugar',
246,
9.0,
39,
4.3,
129,
8,
1,
),
Dessert(
'Eclair with sugar',
271,
16.0,
26,
6.0,
337,
6,
7,
),
Dessert(
'Cupcake with sugar',
314,
3.7,
69,
4.3,
413,
3,
8,
),
Dessert(
'Gingerbread with sugar',
345,
16.0,
51,
3.9,
327,
7,
16,
),
Dessert(
'Jelly Bean with sugar',
364,
0.0,
96,
0.0,
50,
0,
0,
),
Dessert(
'Lollipop with sugar',
401,
0.2,
100,
0.0,
38,
0,
2,
),
Dessert(
'Honeycomd with sugar',
417,
3.2,
89,
6.5,
562,
0,
45,
),
Dessert(
'Donut with sugar',
461,
25.0,
53,
4.9,
326,
2,
22,
),
Dessert(
'Apple pie with sugar',
527,
26.0,
67,
7.0,
54,
12,
6,
),
Dessert(
'Forzen yougurt with honey',
223,
6.0,
36,
4.0,
87,
14,
1,
),
Dessert(
'Ice Cream Sandwich with honey',
301,
9.0,
49,
4.3,
129,
8,
1,
),
Dessert(
'Eclair with honey',
326,
16.0,
36,
6.0,
337,
6,
7,
),
Dessert(
'Cupcake with honey',
369,
3.7,
79,
4.3,
413,
3,
8,
),
Dessert(
'Gignerbread with hone',
420,
16.0,
61,
3.9,
327,
7,
16,
),
Dessert(
'Jelly Bean with honey',
439,
0.0,
106,
0.0,
50,
0,
0,
),
Dessert(
'Lollipop with honey',
456,
0.2,
110,
0.0,
38,
0,
2,
),
Dessert(
'Honeycomd with honey',
472,
3.2,
99,
6.5,
562,
0,
45,
),
Dessert(
'Donut with honey',
516,
25.0,
63,
4.9,
326,
2,
22,
),
Dessert(
'Apple pie with honey',
582,
26.0,
77,
7.0,
54,
12,
6,
),
];
List<Dessert> _dessertsX3 = _desserts.toList()
..addAll(_desserts.map((i) => Dessert('${i.name} x2', i.calories, i.fat,
i.carbs, i.protein, i.sodium, i.calcium, i.iron)))
..addAll(_desserts.map((i) => Dessert('${i.name} x3', i.calories, i.fat,
i.carbs, i.protein, i.sodium, i.calcium, i.iron)));
_showSnackbar(BuildContext context, String text, [Color? color]) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
backgroundColor: color,
duration: const Duration(seconds: 1),
content: Text(text),
));
}

View File

@ -0,0 +1,755 @@
import 'package:data_table_2/data_table_2.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/widgets/core/sk_text_input.dart';
import './helper.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import './data_sources.dart';
import './nav_helper.dart';
// Copyright 2019 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The file was extracted from GitHub: https://github.com/flutter/gallery
// Changes and modifications by Maxim Saplin, 2021
class DataTable2FixedNMDemo extends StatefulWidget {
const DataTable2FixedNMDemo({super.key});
@override
DataTable2FixedNMDemoState createState() => DataTable2FixedNMDemoState();
}
class DataTable2FixedNMDemoState extends State<DataTable2FixedNMDemo> {
bool _sortAscending = true;
int? _sortColumnIndex;
late DessertDataSource _dessertsDataSource;
late DessertDataSourceAsync _asyncDessertsDataSource;
bool _initialized = false;
final ScrollController _controller = ScrollController();
String selectedTableType = dflt;
int _fixedRows = 1;
int _fixedCols = 1;
int _dataItems = 30;
DataRow _getRow(int index, [Color? color]) {
final format = NumberFormat.decimalPercentPattern(
locale: 'zh',
decimalDigits: 0,
);
assert(index >= 0);
if (index >= _desserts.length) throw 'index > _desserts.length';
final dessert = _desserts[index];
return DataRow2.byIndex(
index: index,
// selected: dessert.selected,
color: color != null ? MaterialStateProperty.all(color) : null,
// onSelectChanged: (value) {
// if (dessert.selected != value) {
// dessert.selected = value!;
// setState(() {});
// }
// },
cells: [
DataCell(SkTextInput(textController: TextEditingController())),
DataCell(Text('${dessert.calories}')),
DataCell(Text(dessert.fat.toStringAsFixed(1))),
DataCell(Text('${dessert.carbs}')),
DataCell(Text(dessert.protein.toStringAsFixed(1))),
DataCell(Text('${dessert.sodium}')),
DataCell(Text(format.format(dessert.calcium / 100))),
DataCell(Text(format.format(dessert.iron / 100))),
],
);
}
void selectAll(bool? checked) {
for (final dessert in _desserts) {
dessert.selected = checked ?? false;
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (!_initialized) {
_dessertsDataSource = DessertDataSource(context);
_asyncDessertsDataSource = DessertDataSourceAsync();
_initialized = true;
_dessertsDataSource.addListener(() {
setState(() {});
});
}
}
void _sort<T>(
Comparable<T> Function(Dessert d) getField,
int columnIndex,
bool ascending,
) {
_dessertsDataSource.sort<T>(getField, ascending);
setState(() {
_sortColumnIndex = columnIndex;
_sortAscending = ascending;
});
}
Widget getTableFromSelectedType() {
switch (getCurrentRouteOption(context)) {
case paginatedFixedRowsCols:
{
return getPaginatedDataTable();
}
case asyncPaginatedFixedRowsCols:
{
return getAsyncPaginatedDataTable();
}
default:
{
return getDataTable();
}
}
}
@override
void dispose() {
_dessertsDataSource.dispose();
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(children: [
// _getParamsWidget(context),
Flexible(
fit: FlexFit.tight,
child: Theme(
data: ThemeData(dividerColor: Colors.grey[400]),
child: getTableFromSelectedType()))
]));
}
DataTable2 getDataTable() {
return DataTable2(
scrollController: _controller,
columnSpacing: 0,
bottomMargin: 20,
border: TableBorder.all(width: 1.0, color: AppTheme.dividerColor),
// headingRowColor: MaterialStateProperty.resolveWith(
// (states) => _fixedRows > 0 ? Colors.grey[200] : Colors.transparent),
// headingRowDecoration: BoxDecoration(
// gradient: LinearGradient(
// colors: [
// Colors.grey[400]!,
// Colors.grey[200]!,
// ],
// begin: Alignment.topCenter,
// end: Alignment.bottomCenter,
// ),
// ),
fixedColumnsColor: Colors.grey[100],
// fixedCornerColor: Colors.grey[400],
minWidth: 1000,
fixedTopRows: _fixedRows,
fixedLeftColumns: _fixedCols,
// sortColumnIndex: _sortColumnIndex,
// sortAscending: _sortAscending,
// onSelectAll: (val) => setState(() => selectAll(val)),
columns: [
DataColumn2(
label: const Text('过渡'),
onSort: (columnIndex, ascending) =>
_sort<String>((d) => d.name, columnIndex, ascending),
),
DataColumn2(
label: const Text('名称'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.calories, columnIndex, ascending),
),
DataColumn2(
label: const Text('规格、型号及说明'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.fat, columnIndex, ascending),
),
DataColumn2(
label: const Text('单位'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.carbs, columnIndex, ascending),
),
DataColumn2(
label: const Text('数量'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.protein, columnIndex, ascending),
),
DataColumn2(
label: const Text('备注'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.sodium, columnIndex, ascending),
),
DataColumn2(
label: const Text('单价'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.calcium, columnIndex, ascending),
),
DataColumn2(
label: const Text('总价'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.iron, columnIndex, ascending),
),
],
rows: List<DataRow>.generate(
_dataItems, (index) => _getRow(index, Colors.transparent)));
}
Widget getPaginatedDataTable() {
return PaginatedDataTable2(
scrollController: _controller,
columnSpacing: 0,
horizontalMargin: 12,
border: TableBorder.all(width: 1.0, color: Colors.grey),
headingRowColor: MaterialStateProperty.resolveWith(
(states) => _fixedRows > 0 ? Colors.grey[200] : Colors.transparent),
fixedColumnsColor: Colors.grey[300],
fixedCornerColor: Colors.grey[400],
minWidth: 1000,
fixedTopRows: _fixedRows,
fixedLeftColumns: _fixedCols,
sortColumnIndex: _sortColumnIndex,
sortAscending: _sortAscending,
onSelectAll: (val) => setState(() => selectAll(val)),
columns: [
DataColumn2(
label: const Text('Desert'),
size: ColumnSize.S,
onSort: (columnIndex, ascending) =>
_sort<String>((d) => d.name, columnIndex, ascending),
),
DataColumn2(
label: const Text('Calories'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.calories, columnIndex, ascending),
),
DataColumn2(
label: const Text('Fat (gm)'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.fat, columnIndex, ascending),
),
DataColumn2(
label: const Text('Carbs (gm)'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.carbs, columnIndex, ascending),
),
DataColumn2(
label: const Text('Protein (gm)'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.protein, columnIndex, ascending),
),
DataColumn2(
label: const Text('Sodium (mg)'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.sodium, columnIndex, ascending),
),
DataColumn2(
label: const Text('Calcium (%)'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.calcium, columnIndex, ascending),
),
DataColumn2(
label: const Text('Iron (%)'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.iron, columnIndex, ascending),
),
],
source: _dessertsDataSource);
}
Widget getAsyncPaginatedDataTable() {
return AsyncPaginatedDataTable2(
scrollController: _controller,
columnSpacing: 0,
horizontalMargin: 12,
border: TableBorder.all(width: 1.0, color: Colors.grey),
headingRowColor: MaterialStateProperty.resolveWith(
(states) => _fixedRows > 0 ? Colors.grey[200] : Colors.transparent),
fixedColumnsColor: Colors.grey[300],
fixedCornerColor: Colors.grey[400],
minWidth: 1000,
fixedTopRows: _fixedRows,
fixedLeftColumns: _fixedCols,
sortColumnIndex: _sortColumnIndex,
sortAscending: _sortAscending,
onSelectAll: (val) => setState(() => selectAll(val)),
columns: [
DataColumn2(
label: const Text('Desert'),
size: ColumnSize.S,
onSort: (columnIndex, ascending) =>
_sort<String>((d) => d.name, columnIndex, ascending),
),
DataColumn2(
label: const Text('Calories'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.calories, columnIndex, ascending),
),
DataColumn2(
label: const Text('Fat (gm)'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.fat, columnIndex, ascending),
),
DataColumn2(
label: const Text('Carbs (gm)'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.carbs, columnIndex, ascending),
),
DataColumn2(
label: const Text('Protein (gm)'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.protein, columnIndex, ascending),
),
DataColumn2(
label: const Text('Sodium (mg)'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.sodium, columnIndex, ascending),
),
DataColumn2(
label: const Text('Calcium (%)'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.calcium, columnIndex, ascending),
),
DataColumn2(
label: const Text('Iron (%)'),
size: ColumnSize.S,
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.iron, columnIndex, ascending),
),
],
source: _asyncDessertsDataSource);
}
Theme _getParamsWidget(BuildContext context) {
const double col1 = 130;
const double col2 = 210;
return Theme(
data: blackSlider(context),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 36,
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
SizedBox(
width: col1, child: Text('Fixed rows ($_fixedRows)')),
SizedBox(
width: col2,
child: Slider(
value: _fixedRows.toDouble(),
min: 0,
max: 10,
divisions: 10,
onChanged: (val) {
setState(() {
_fixedRows = val.toInt();
});
}))
],
)),
SizedBox(
height: 36,
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
SizedBox(
width: col1,
child: Text('Fixed columns ($_fixedCols)')),
SizedBox(
width: col2,
child: Slider(
value: _fixedCols.toDouble(),
min: 0,
max: 10,
divisions: 10,
onChanged: (val) {
setState(() {
_fixedCols = val.toInt();
});
}))
],
)),
SizedBox(
height: 36,
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
SizedBox(
width: col1, child: Text('Data items ($_dataItems)')),
SizedBox(
width: col2,
child: Slider(
value: _dataItems.toDouble(),
min: 0,
max: 30,
divisions: 10,
onChanged: (val) {
setState(() {
_dataItems = val.toInt();
});
}))
],
))
],
));
}
}
List<Dessert> _desserts = <Dessert>[
Dessert(
'Frozen Yogurt',
159,
6.0,
24,
4.0,
87,
14,
1,
),
Dessert(
'Ice Cream Sandwich',
237,
9.0,
37,
4.3,
129,
8,
1,
),
Dessert(
'Eclair',
262,
16.0,
24,
6.0,
337,
6,
7,
),
Dessert(
'Cupcake',
305,
3.7,
67,
4.3,
413,
3,
8,
),
Dessert(
'Gingerbread',
356,
16.0,
49,
3.9,
327,
7,
16,
),
Dessert(
'Jelly Bean',
375,
0.0,
94,
0.0,
50,
0,
0,
),
Dessert(
'Lollipop',
392,
0.2,
98,
0.0,
38,
0,
2,
),
Dessert(
'Honeycomb',
408,
3.2,
87,
6.5,
562,
0,
45,
),
Dessert(
'Donut',
452,
25.0,
51,
4.9,
326,
2,
22,
),
Dessert(
'Apple Pie',
518,
26.0,
65,
7.0,
54,
12,
6,
),
Dessert(
'Frozen Yougurt with sugar',
168,
6.0,
26,
4.0,
87,
14,
1,
),
Dessert(
'Ice Cream Sandwich with sugar',
246,
9.0,
39,
4.3,
129,
8,
1,
),
Dessert(
'Eclair with sugar',
271,
16.0,
26,
6.0,
337,
6,
7,
),
Dessert(
'Cupcake with sugar',
314,
3.7,
69,
4.3,
413,
3,
8,
),
Dessert(
'Gingerbread with sugar',
345,
16.0,
51,
3.9,
327,
7,
16,
),
Dessert(
'Jelly Bean with sugar',
364,
0.0,
96,
0.0,
50,
0,
0,
),
Dessert(
'Lollipop with sugar',
401,
0.2,
100,
0.0,
38,
0,
2,
),
Dessert(
'Honeycomd with sugar',
417,
3.2,
89,
6.5,
562,
0,
45,
),
Dessert(
'Donut with sugar',
461,
25.0,
53,
4.9,
326,
2,
22,
),
Dessert(
'Apple pie with sugar',
527,
26.0,
67,
7.0,
54,
12,
6,
),
Dessert(
'Forzen yougurt with honey',
223,
6.0,
36,
4.0,
87,
14,
1,
),
Dessert(
'Ice Cream Sandwich with honey',
301,
9.0,
49,
4.3,
129,
8,
1,
),
Dessert(
'Eclair with honey',
326,
16.0,
36,
6.0,
337,
6,
7,
),
Dessert(
'Cupcake with honey',
369,
3.7,
79,
4.3,
413,
3,
8,
),
Dessert(
'Gignerbread with hone',
420,
16.0,
61,
3.9,
327,
7,
16,
),
Dessert(
'Jelly Bean with honey',
439,
0.0,
106,
0.0,
50,
0,
0,
),
Dessert(
'Lollipop with honey',
456,
0.2,
110,
0.0,
38,
0,
2,
),
Dessert(
'Honeycomd with honey',
472,
3.2,
99,
6.5,
562,
0,
45,
),
Dessert(
'Donut with honey',
516,
25.0,
63,
4.9,
326,
2,
22,
),
Dessert(
'Apple pie with honey',
582,
26.0,
77,
7.0,
54,
12,
6,
),
];

View File

@ -0,0 +1,176 @@
import 'package:flutter/material.dart';
import 'dart:math';
ThemeData blackSlider(BuildContext context) {
return Theme.of(context).copyWith(
sliderTheme: SliderThemeData(
rangeThumbShape:
const RectRangeSliderThumbShape(enabledThumbRadius: 8),
thumbShape: const RectSliderThumbShape(enabledThumbRadius: 8),
thumbColor: Colors.grey[800],
activeTrackColor: Colors.grey[700],
inactiveTrackColor: Colors.grey[400],
activeTickMarkColor: Colors.white,
inactiveTickMarkColor: Colors.white));
}
class RectRangeSliderThumbShape extends RangeSliderThumbShape {
const RectRangeSliderThumbShape({
this.enabledThumbRadius = 10.0,
this.disabledThumbRadius,
this.elevation = 1.0,
this.pressedElevation = 6.0,
});
final double enabledThumbRadius;
final double? disabledThumbRadius;
double get _disabledThumbRadius => disabledThumbRadius ?? enabledThumbRadius;
final double elevation;
final double pressedElevation;
@override
Size getPreferredSize(bool isEnabled, bool isDiscrete) {
return Size.fromRadius(
isEnabled == true ? enabledThumbRadius : _disabledThumbRadius);
}
@override
void paint(
PaintingContext context,
Offset center, {
required Animation<double> activationAnimation,
required Animation<double> enableAnimation,
bool isDiscrete = false,
bool isEnabled = false,
bool? isOnTop,
required SliderThemeData sliderTheme,
TextDirection? textDirection,
Thumb? thumb,
bool? isPressed,
}) {
assert(sliderTheme.showValueIndicator != null);
assert(sliderTheme.overlappingShapeStrokeColor != null);
final Canvas canvas = context.canvas;
final Tween<double> radiusTween = Tween<double>(
begin: _disabledThumbRadius,
end: enabledThumbRadius,
);
final ColorTween colorTween = ColorTween(
begin: sliderTheme.disabledThumbColor,
end: sliderTheme.thumbColor,
);
final double radius = radiusTween.evaluate(enableAnimation);
final Tween<double> elevationTween = Tween<double>(
begin: elevation,
end: pressedElevation,
);
if (isOnTop ?? false) {
final Paint strokePaint = Paint()
..color = sliderTheme.overlappingShapeStrokeColor!
..strokeWidth = 1.0
..style = PaintingStyle.stroke;
canvas.drawRect(
Rect.fromCenter(
center: center, width: 2 * radius, height: 2 * radius),
strokePaint);
}
final Color color = colorTween.evaluate(enableAnimation)!;
final double evaluatedElevation =
isPressed! ? elevationTween.evaluate(activationAnimation) : elevation;
final Path shadowPath = Path()
..addArc(
Rect.fromCenter(
center: center, width: 2 * radius, height: 2 * radius),
0,
pi * 2);
canvas.drawShadow(shadowPath, Colors.black, evaluatedElevation, true);
canvas.drawRect(
Rect.fromCenter(center: center, width: 2 * radius, height: 2 * radius),
Paint()..color = color,
);
}
}
class RectSliderThumbShape extends SliderComponentShape {
const RectSliderThumbShape({
this.enabledThumbRadius = 10.0,
this.disabledThumbRadius,
this.elevation = 1.0,
this.pressedElevation = 6.0,
});
final double enabledThumbRadius;
final double? disabledThumbRadius;
double get _disabledThumbRadius => disabledThumbRadius ?? enabledThumbRadius;
final double elevation;
final double pressedElevation;
@override
Size getPreferredSize(bool isEnabled, bool isDiscrete) {
return Size.fromRadius(
isEnabled == true ? enabledThumbRadius : _disabledThumbRadius);
}
@override
void paint(
PaintingContext context,
Offset center, {
required Animation<double> activationAnimation,
required Animation<double> enableAnimation,
required bool isDiscrete,
required TextPainter labelPainter,
required RenderBox parentBox,
required SliderThemeData sliderTheme,
required TextDirection textDirection,
required double value,
required double textScaleFactor,
required Size sizeWithOverflow,
}) {
assert(sliderTheme.disabledThumbColor != null);
assert(sliderTheme.thumbColor != null);
final Canvas canvas = context.canvas;
final Tween<double> radiusTween = Tween<double>(
begin: _disabledThumbRadius,
end: enabledThumbRadius,
);
final ColorTween colorTween = ColorTween(
begin: sliderTheme.disabledThumbColor,
end: sliderTheme.thumbColor,
);
final Color color = colorTween.evaluate(enableAnimation)!;
final double radius = radiusTween.evaluate(enableAnimation);
final Tween<double> elevationTween = Tween<double>(
begin: elevation,
end: pressedElevation,
);
final double evaluatedElevation =
elevationTween.evaluate(activationAnimation);
final Path path = Path()
..addArc(
Rect.fromCenter(
center: center, width: 2 * radius, height: 2 * radius),
0,
pi * 2);
canvas.drawShadow(path, Colors.black, evaluatedElevation, true);
canvas.drawRect(
Rect.fromCenter(center: center, width: 2 * radius, height: 2 * radius),
Paint()..color = color,
);
}
}

View File

@ -0,0 +1,71 @@
import 'package:flutter/material.dart';
/// Route options are used to configure certain features of
/// the given example
String getCurrentRouteOption(BuildContext context) {
var isEmpty = ModalRoute.of(context) != null &&
ModalRoute.of(context)!.settings.arguments != null &&
ModalRoute.of(context)!.settings.arguments is String
? ModalRoute.of(context)!.settings.arguments as String
: '';
return isEmpty;
}
// Route options
const dflt = 'Default';
const noData = 'No data';
const autoRows = 'Auto rows';
const showBordersWithZebraStripes = 'Borders with Zebra';
const custPager = 'Custom pager';
const defaultSorting = 'Default sorting';
const selectAllPage = 'Select all at page';
const rowTaps = 'Row Taps';
const rowHeightOverrides = 'Row height overrides';
const fixedColumnWidth = 'Fixed column width';
const dataTable2 = 'DataTable2';
const paginatedFixedRowsCols = 'PaginatedDataTable2';
const asyncPaginatedFixedRowsCols = 'AsyncPaginatedDataTable2';
const custArrows = 'Custom sort arrows';
const asyncErrors =
"Errors/Retries"; // Async sample that emulates network error and allow retrying load operation
const goToLast =
"Start at last page"; // Used by async example, navigates to the very last page upon opening the screen
const rounded = 'Rounded style';
/// Configurations available to given example routes
const Map<String, List<String>> routeOptions = {
'/datatable2': [
dflt,
noData,
showBordersWithZebraStripes,
fixedColumnWidth,
rowTaps,
rowHeightOverrides,
custArrows,
rounded
],
'/paginated2': [dflt, noData, autoRows, custPager, defaultSorting],
'/datatable2fixedmn': [
dataTable2,
paginatedFixedRowsCols,
asyncPaginatedFixedRowsCols
],
'/asyncpaginated2': [
dflt,
noData,
selectAllPage,
autoRows,
asyncErrors,
goToLast,
custPager
],
};
List<String>? getOptionsForRoute(String route) {
if (!routeOptions.containsKey(route)) {
return null;
}
return routeOptions[route];
}

View File

@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
import 'package:sk_base_mobile/screens/sale_quotation/components/data_table.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/widgets/sk_appbar.dart';
class SaleQuotationPage extends StatelessWidget {
const SaleQuotationPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const SkAppbar(title: '报价计算'),
body: buildBody(),
);
}
Widget buildBody() {
return DataTable2FixedNMDemo();
// return SingleChildScrollView(
// child: Column(
// children: [Text('11')],
// ),
// );
// return SliverStickyHeader.builder(
// builder: (context, state) => Container(
// height: 60.0,
// color: (state.isPinned ? Colors.pink : Colors.lightBlue)
// .withOpacity(1.0 - state.scrollPercentage),
// padding: EdgeInsets.symmetric(horizontal: 16.0),
// alignment: Alignment.centerLeft,
// child: Text(
// 'Header #1',
// style: const TextStyle(color: Colors.white),
// ),
// ),
// sliver: SliverList(
// delegate: SliverChildBuilderDelegate(
// (context, i) => ListTile(
// leading: CircleAvatar(
// child: Text('0'),
// ),
// title: Text('List tile #$i'),
// ),
// childCount: 4,
// ),
// ),
// );
}
}

View File

@ -1,28 +1,16 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:flutter_svg/svg.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/constants/router.dart';
import 'package:sk_base_mobile/screens/workbench/workbench_controller.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/util/snack_bar.util.dart';
class WorkBenchModel {
final String title;
final String route;
WorkBenchModel({required this.title, required this.route});
}
class WorkBenchPage extends StatelessWidget {
WorkBenchPage({super.key});
final List<WorkBenchModel> works = [
WorkBenchModel(title: '库存', route: '/inventory'),
WorkBenchModel(title: '产品', route: '/product'),
WorkBenchModel(title: '合同', route: '/contract'),
WorkBenchModel(title: '人事', route: '/personnel'),
WorkBenchModel(title: '公车', route: '/finance'),
WorkBenchModel(title: '任务', route: '/task_manage'),
WorkBenchModel(title: '报表', route: '/report'),
];
final controller = Get.put(WorkBenchController());
@override
Widget build(BuildContext context) {
@ -33,86 +21,130 @@ class WorkBenchPage extends StatelessWidget {
'工作台',
),
),
body: SingleChildScrollView(
child: Column(children: [
Container(
padding: EdgeInsets.all(ScreenAdaper.width(20)),
child: GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: works.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: Get.width > 800 ? 5 : 3,
crossAxisSpacing: ScreenAdaper.width(20),
mainAxisSpacing: ScreenAdaper.height(20),
childAspectRatio: 1.0),
itemBuilder: (BuildContext context, int index) {
return buildCard(index);
},
),
)
])),
body: buildList()
// Container(
// padding: EdgeInsets.all(ScreenAdaper.width(20)),
// child: GridView.builder(
// shrinkWrap: true,
// physics: const NeverScrollableScrollPhysics(),
// itemCount: works.length,
// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
// crossAxisCount: Get.width > 800 ? 5 : 3,
// crossAxisSpacing: ScreenAdaper.width(20),
// mainAxisSpacing: ScreenAdaper.height(20),
// childAspectRatio: 1.0),
// itemBuilder: (BuildContext context, int index) {
// return buildCard(index);
// },
// ),
// )
);
}
Widget buildCard(int index) {
Widget buildList() {
return MasonryGridView.count(
padding: EdgeInsets.only(top: ScreenAdaper.width(20)),
crossAxisCount: Get.width > 800 ? 6 : 4,
itemCount: controller.menus.length,
itemBuilder: (context, index) {
return buildItem(
index,
);
},
);
}
Widget buildItem(int index) {
return InkWell(
onTap: () {
final route = RouteConfig.getPages
.map((e) => e.name)
.firstWhereOrNull((name) => name == works[index].route);
.firstWhereOrNull((name) => name == controller.menus[index].route);
if (route != null) {
Get.toNamed(works[index].route);
Get.toNamed(controller.menus[index].route);
} else {
SnackBarUtil().info('功能待开发。');
}
},
child: Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
gradient: const LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [AppTheme.primaryColorLight, AppTheme.primaryColor]),
boxShadow: [
BoxShadow(
color: AppTheme.black.withOpacity(0.4),
offset: const Offset(0, 0),
blurRadius: 1,
spreadRadius: 1)
],
),
child: Stack(
children: [
// Image.asset(works[index].icon),
Center(
child: Stack(
alignment: Alignment.center,
padding: EdgeInsets.only(
top: ScreenAdaper.height(20), bottom: ScreenAdaper.height(20)),
child: Column(
children: [
Text(
works[index].title,
style: TextStyle(
letterSpacing: ScreenAdaper.width(10),
fontSize: ScreenAdaper.height(40),
fontWeight: FontWeight.bold,
foreground: Paint()
..style = PaintingStyle.stroke
..strokeWidth = 5
..color = Colors.black),
SvgPicture.asset(
'assets/icons/${controller.menus[index].icon}',
width: ScreenAdaper.width(100),
height: ScreenAdaper.width(100),
),
Text(
works[index].title,
style: TextStyle(
letterSpacing: ScreenAdaper.width(10),
color: Colors.white,
fontSize: ScreenAdaper.height(40),
fontWeight: FontWeight.bold),
SizedBox(
height: ScreenAdaper.height(10),
),
Text(controller.menus[index].title)
],
),
)
],
)));
),
);
}
// Widget buildCard(int index) {
// return InkWell(
// onTap: () {
// final route = RouteConfig.getPages
// .map((e) => e.name)
// .firstWhereOrNull((name) => name == works[index].route);
// if (route != null) {
// Get.toNamed(works[index].route);
// } else {
// SnackBarUtil().info('功能待开发。');
// }
// },
// child: Container(
// clipBehavior: Clip.antiAlias,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(10),
// gradient: const LinearGradient(
// begin: Alignment.centerLeft,
// end: Alignment.centerRight,
// colors: [AppTheme.primaryColorLight, AppTheme.primaryColor]),
// boxShadow: [
// BoxShadow(
// color: AppTheme.black.withOpacity(0.4),
// offset: const Offset(0, 0),
// blurRadius: 1,
// spreadRadius: 1)
// ],
// ),
// child: Stack(
// children: [
// // Image.asset(works[index].icon),
// Center(
// child: Stack(
// alignment: Alignment.center,
// children: [
// Text(
// works[index].title,
// style: TextStyle(
// letterSpacing: ScreenAdaper.width(10),
// fontSize: ScreenAdaper.height(40),
// fontWeight: FontWeight.bold,
// foreground: Paint()
// ..style = PaintingStyle.stroke
// ..strokeWidth = 5
// ..color = Colors.black),
// ),
// Text(
// works[index].title,
// style: TextStyle(
// letterSpacing: ScreenAdaper.width(10),
// color: Colors.white,
// fontSize: ScreenAdaper.height(40),
// fontWeight: FontWeight.bold),
// ),
// ],
// ),
// )
// ],
// )));
// }
}

View File

@ -1,9 +1,20 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/models/workbench.model.dart';
class WorkBenchController extends GetxController {
late final AnimationController animationController;
final List<WorkBenchModel> menus = [
WorkBenchModel(title: '库存', route: '/inventory', icon: 'inventory.svg'),
WorkBenchModel(title: '产品', route: '/product', icon: 'product.svg'),
WorkBenchModel(title: '合同', route: '/contract', icon: 'contract.svg'),
WorkBenchModel(title: '人事', route: '/hr', icon: 'hr.svg'),
WorkBenchModel(title: '公车', route: '/vehicle', icon: 'vehicle.svg'),
WorkBenchModel(title: '任务', route: '/task_manage', icon: 'task_manage.svg'),
WorkBenchModel(title: '报表', route: '/report', icon: 'report.svg'),
WorkBenchModel(
title: '报价计算', route: '/sale_quotation', icon: 'sale_quotation.svg'),
];
@override
void onClose() {
animationController.dispose();

View File

@ -2,7 +2,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/widgets/core/zt_bottomsheet_picker.dart';
import 'package:sk_base_mobile/widgets/core/sk_bottomsheet_picker.dart';
class ModalUtil {
static Future<bool> showWarningDialog(
@ -55,7 +55,7 @@ class ModalUtil {
showCupertinoModalPopup(
context: Get.overlayContext!,
builder: (BuildContext context) {
return ZtBottomSheetPicker(
return SkBottomSheetPicker(
title: title,
onChanged: onChanged,
firstLevel: firstLevel,

View File

@ -5,14 +5,14 @@ import 'package:sk_base_mobile/util/logger_util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
// ignore: must_be_immutable
class ZtBaseDatePicker extends StatelessWidget {
class SkBaseDatePicker extends StatelessWidget {
final Function(String)? onDateTimeChanged;
late final FixedExtentScrollController yearController;
late final FixedExtentScrollController monthController;
late final FixedExtentScrollController dayController;
DateTime? initialDate = DateTime.now();
DateTime? endDate = DateTime.now();
ZtBaseDatePicker(
SkBaseDatePicker(
{super.key, this.onDateTimeChanged, this.initialDate, this.endDate});
@override

View File

@ -4,7 +4,7 @@ import 'package:get/get.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class ZtBottomSheetPicker extends StatelessWidget {
class SkBottomSheetPicker extends StatelessWidget {
final Function? onChanged;
final FixedExtentScrollController firstLevelControlller =
FixedExtentScrollController();
@ -18,7 +18,7 @@ class ZtBottomSheetPicker extends StatelessWidget {
final double? itemHeight;
final double? popupHeight;
final String title;
ZtBottomSheetPicker(
SkBottomSheetPicker(
{super.key,
this.title = '请选择',
required this.onChanged,

View File

@ -2,14 +2,14 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class ZtDatePicker extends StatelessWidget {
class SkDatePicker extends StatelessWidget {
final TextEditingController textController;
final VoidCallback? onClear;
final Function? onDateSelected;
final bool isRequired;
final String labelText;
const ZtDatePicker({
const SkDatePicker({
super.key,
this.onClear,
this.onDateSelected,

View File

@ -3,7 +3,7 @@ import 'package:flutter/services.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class ZtNumberInput extends StatelessWidget {
class SkNumberInput extends StatelessWidget {
final TextEditingController textController;
final VoidCallback? onTap;
final Function(String)? onChanged;
@ -11,7 +11,7 @@ class ZtNumberInput extends StatelessWidget {
final bool isRequired;
final String labelText;
final String? hint;
const ZtNumberInput(
const SkNumberInput(
{super.key,
required this.textController,
this.onTap,

View File

@ -3,7 +3,7 @@ import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class ZtSearchSelect<T> extends StatelessWidget {
class SkSearchSelect<T> extends StatelessWidget {
final TextEditingController textController;
final Function? suggestionsCallback;
final void Function(T)? onSelected;
@ -13,7 +13,7 @@ class ZtSearchSelect<T> extends StatelessWidget {
final EdgeInsetsGeometry? contentPadding;
final bool isRequired;
final Widget Function(BuildContext, T) itemBuilder;
const ZtSearchSelect(
const SkSearchSelect(
{super.key,
required this.textController,
required this.itemBuilder,

View File

@ -2,14 +2,14 @@ import 'package:flutter/material.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class ZtTextInput extends StatelessWidget {
class SkTextInput extends StatelessWidget {
final TextEditingController textController;
final VoidCallback? onTap;
final bool isRequired;
final String labelText;
final String? hint;
final bool isTextArea;
const ZtTextInput({
const SkTextInput({
super.key,
required this.textController,
this.onTap,

View File

@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
class SkAppbar extends StatelessWidget implements PreferredSizeWidget {
final String title;
const SkAppbar({super.key, required this.title});
@override
Widget build(BuildContext context) {
return AppBar(title: Text(title));
}
@override
Size get preferredSize => Size.fromHeight(kToolbarHeight);
}

View File

@ -161,6 +161,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.6"
data_table_2:
dependency: "direct main"
description:
name: data_table_2
sha256: e403de6d9a58dddf27700114b614ea8ea5aa8442d7fbdfbe8b3d11b0512e7a49
url: "https://pub.dev"
source: hosted
version: "2.5.12"
date_format:
dependency: "direct main"
description:
@ -366,6 +374,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.0"
flutter_sticky_header:
dependency: "direct main"
description:
name: flutter_sticky_header
sha256: "017f398fbb45a589e01491861ca20eb6570a763fd9f3888165a978e11248c709"
url: "https://pub.dev"
source: hosted
version: "0.6.5"
flutter_svg:
dependency: "direct main"
description:
name: flutter_svg
sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2"
url: "https://pub.dev"
source: hosted
version: "2.0.10+1"
flutter_test:
dependency: "direct dev"
description: flutter
@ -592,6 +616,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.8.3"
path_parsing:
dependency: transitive
description:
name: path_parsing
sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf
url: "https://pub.dev"
source: hosted
version: "1.0.1"
path_provider:
dependency: "direct main"
description:
@ -957,6 +989,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.3.3"
value_layout_builder:
dependency: transitive
description:
name: value_layout_builder
sha256: "98202ec1807e94ac72725b7f0d15027afde513c55c69ff3f41bcfccb950831bc"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
vector_graphics:
dependency: transitive
description:
name: vector_graphics
sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3"
url: "https://pub.dev"
source: hosted
version: "1.1.11+1"
vector_graphics_codec:
dependency: transitive
description:
name: vector_graphics_codec
sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da
url: "https://pub.dev"
source: hosted
version: "1.1.11+1"
vector_graphics_compiler:
dependency: transitive
description:
name: vector_graphics_compiler
sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81"
url: "https://pub.dev"
source: hosted
version: "1.1.11+1"
vector_math:
dependency: transitive
description:

View File

@ -61,6 +61,9 @@ dependencies:
image_gallery_saver: ^2.0.3
flutter_staggered_grid_view: ^0.7.0
flutter_native_splash: ^2.3.11
flutter_svg: ^2.0.10+1
flutter_sticky_header: ^0.6.5
data_table_2: ^2.5.12
dev_dependencies:
flutter_test:
sdk: flutter