feat: 1
This commit is contained in:
parent
27a92960c5
commit
a010670dad
|
@ -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';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:sk_base_mobile/screens/new_inventory_inout/components/inventory_search.dart';
|
||||
import 'package:sk_base_mobile/widgets/sk_appbar.dart';
|
||||
|
||||
class InventoryPage extends StatelessWidget {
|
||||
const InventoryPage({super.key});
|
||||
|
@ -7,7 +8,7 @@ class InventoryPage extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('库存管理')),
|
||||
appBar: const SkAppbar(title: '库存管理'),
|
||||
body: InventorySearch(),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,20 +16,19 @@ class LandingPage extends StatelessWidget {
|
|||
return Material(
|
||||
child: Stack(children: [
|
||||
const BackColors(),
|
||||
Scaffold(
|
||||
floatingActionButtonLocation:
|
||||
FloatingActionButtonLocation.centerDocked,
|
||||
// floatingActionButton: [0].indexWhere(
|
||||
// (item) => item == controller.currentIndex.value) >
|
||||
// -1
|
||||
// ? FloatingCreateButton()
|
||||
// : null,
|
||||
floatingActionButton: FloatingCreateButton(),
|
||||
bottomNavigationBar: BottomNavBar(),
|
||||
backgroundColor: Colors.transparent,
|
||||
body: Obx(() => controller.pages[controller.currentIndex.value]),
|
||||
)
|
||||
Obx(() => Scaffold(
|
||||
floatingActionButtonLocation:
|
||||
FloatingActionButtonLocation.centerDocked,
|
||||
floatingActionButton: buildBody(),
|
||||
bottomNavigationBar: BottomNavBar(),
|
||||
backgroundColor: Colors.transparent,
|
||||
body: Obx(() => controller.pages[controller.currentIndex.value]),
|
||||
))
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget? buildBody() {
|
||||
return controller.currentIndex.value == 0 ? FloatingCreateButton() : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -192,178 +192,180 @@ class InventorySearch extends StatelessWidget {
|
|||
// 库存列表
|
||||
Widget buildInventoryList() {
|
||||
final textStyle = TextStyle(fontSize: ScreenAdaper.height(25));
|
||||
return Obx(() => SmartRefresher(
|
||||
enablePullDown: true,
|
||||
enablePullUp: true,
|
||||
controller: controller.refreshController,
|
||||
onLoading: controller.onLoading,
|
||||
onRefresh: controller.onRefresh,
|
||||
child: controller.refreshController.isLoading
|
||||
? const SizedBox()
|
||||
: controller.inventories.isEmpty
|
||||
? const Center(
|
||||
child: Empty(text: '暂无库存'),
|
||||
)
|
||||
: !ScreenAdaper.isLandspace()
|
||||
? buildPortraitList()
|
||||
: Table(columnWidths: {
|
||||
0: const MinColumnWidth(
|
||||
FixedColumnWidth(80), FixedColumnWidth(80)),
|
||||
1: MinColumnWidth(
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 5),
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 5)),
|
||||
2: FlexColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 4),
|
||||
3: FlexColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 4),
|
||||
4: MinColumnWidth(
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 5),
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 5)),
|
||||
5: MinColumnWidth(
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 6),
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 6)),
|
||||
}, children: [
|
||||
// table header
|
||||
TableRow(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: AppTheme.dividerColor))),
|
||||
children: [
|
||||
TableCell(
|
||||
verticalAlignment:
|
||||
TableCellVerticalAlignment.middle,
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
height: ScreenAdaper.height(60),
|
||||
return Obx(() => SafeArea(
|
||||
child: SmartRefresher(
|
||||
enablePullDown: true,
|
||||
enablePullUp: true,
|
||||
controller: controller.refreshController,
|
||||
onLoading: controller.onLoading,
|
||||
onRefresh: controller.onRefresh,
|
||||
child: controller.refreshController.isLoading
|
||||
? const SizedBox()
|
||||
: controller.inventories.isEmpty
|
||||
? const Center(
|
||||
child: Empty(text: '暂无库存'),
|
||||
)
|
||||
: !ScreenAdaper.isLandspace()
|
||||
? buildPortraitList()
|
||||
: Table(columnWidths: {
|
||||
0: const MinColumnWidth(
|
||||
FixedColumnWidth(80), FixedColumnWidth(80)),
|
||||
1: MinColumnWidth(
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 5),
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 5)),
|
||||
2: FlexColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 4),
|
||||
3: FlexColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 4),
|
||||
4: MinColumnWidth(
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 5),
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 5)),
|
||||
5: MinColumnWidth(
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 6),
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 6)),
|
||||
}, children: [
|
||||
// table header
|
||||
TableRow(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: AppTheme.dividerColor))),
|
||||
children: [
|
||||
TableCell(
|
||||
verticalAlignment:
|
||||
TableCellVerticalAlignment.middle,
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
height: ScreenAdaper.height(60),
|
||||
child: Text(
|
||||
'库存编号',
|
||||
style: listTitleTextStyle,
|
||||
),
|
||||
)),
|
||||
TableCell(
|
||||
verticalAlignment:
|
||||
TableCellVerticalAlignment.middle,
|
||||
child: Text(
|
||||
'所属项目',
|
||||
style: listTitleTextStyle,
|
||||
)),
|
||||
TableCell(
|
||||
verticalAlignment:
|
||||
TableCellVerticalAlignment.middle,
|
||||
child: Text(
|
||||
'产品名称',
|
||||
style: listTitleTextStyle,
|
||||
)),
|
||||
TableCell(
|
||||
verticalAlignment:
|
||||
TableCellVerticalAlignment.middle,
|
||||
child: Text(
|
||||
'库存编号',
|
||||
'规格',
|
||||
style: listTitleTextStyle,
|
||||
),
|
||||
)),
|
||||
TableCell(
|
||||
verticalAlignment:
|
||||
TableCellVerticalAlignment.middle,
|
||||
child: Text(
|
||||
'所属项目',
|
||||
style: listTitleTextStyle,
|
||||
)),
|
||||
TableCell(
|
||||
verticalAlignment:
|
||||
TableCellVerticalAlignment.middle,
|
||||
child: Text(
|
||||
'产品名称',
|
||||
style: listTitleTextStyle,
|
||||
)),
|
||||
TableCell(
|
||||
verticalAlignment:
|
||||
TableCellVerticalAlignment.middle,
|
||||
child: Text(
|
||||
'规格',
|
||||
style: listTitleTextStyle,
|
||||
),
|
||||
),
|
||||
TableCell(
|
||||
verticalAlignment:
|
||||
TableCellVerticalAlignment.middle,
|
||||
child: Text(
|
||||
'单价',
|
||||
style: listTitleTextStyle,
|
||||
),
|
||||
),
|
||||
TableCell(
|
||||
verticalAlignment:
|
||||
TableCellVerticalAlignment.middle,
|
||||
child: Text(
|
||||
'数量',
|
||||
textAlign: TextAlign.right,
|
||||
style: listTitleTextStyle,
|
||||
),
|
||||
),
|
||||
]),
|
||||
...controller.inventories.map((itemData) {
|
||||
return TableRow(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: AppTheme.dividerColor))),
|
||||
children: [
|
||||
buildTableCell(
|
||||
Text(
|
||||
itemData.inventoryNumber!,
|
||||
style: textStyle,
|
||||
),
|
||||
TableCell(
|
||||
verticalAlignment:
|
||||
TableCellVerticalAlignment.middle,
|
||||
child: Text(
|
||||
'单价',
|
||||
style: listTitleTextStyle,
|
||||
),
|
||||
itemData: itemData),
|
||||
|
||||
// 入库时所属项目
|
||||
buildTableCell(
|
||||
Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
),
|
||||
TableCell(
|
||||
verticalAlignment:
|
||||
TableCellVerticalAlignment.middle,
|
||||
child: Text(
|
||||
'数量',
|
||||
textAlign: TextAlign.right,
|
||||
style: listTitleTextStyle,
|
||||
),
|
||||
),
|
||||
]),
|
||||
...controller.inventories.map((itemData) {
|
||||
return TableRow(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: AppTheme.dividerColor))),
|
||||
children: [
|
||||
buildTableCell(
|
||||
Text(
|
||||
'${itemData.project?.name}',
|
||||
itemData.inventoryNumber!,
|
||||
style: textStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
itemData: itemData),
|
||||
// 产品
|
||||
buildTableCell(
|
||||
Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
itemData: itemData),
|
||||
|
||||
// 入库时所属项目
|
||||
buildTableCell(
|
||||
Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${itemData.project?.name}',
|
||||
style: textStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
itemData: itemData),
|
||||
// 产品
|
||||
buildTableCell(
|
||||
Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${itemData.product?.name}',
|
||||
style: textStyle,
|
||||
),
|
||||
Text(
|
||||
'${itemData.product?.company?.name}',
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
ScreenAdaper.height(15),
|
||||
color: AppTheme.grey),
|
||||
)
|
||||
],
|
||||
),
|
||||
itemData: itemData),
|
||||
// 规格
|
||||
buildTableCell(
|
||||
Text(
|
||||
'${itemData.product?.name}',
|
||||
itemData.product
|
||||
?.productSpecification ??
|
||||
'',
|
||||
style: textStyle),
|
||||
itemData: itemData),
|
||||
// 单价
|
||||
buildTableCell(
|
||||
Text(
|
||||
'¥${double.parse('${itemData.unitPrice}')}',
|
||||
style: textStyle,
|
||||
),
|
||||
Text(
|
||||
'${itemData.product?.company?.name}',
|
||||
style: TextStyle(
|
||||
fontSize: ScreenAdaper.height(15),
|
||||
color: AppTheme.grey),
|
||||
)
|
||||
],
|
||||
),
|
||||
itemData: itemData),
|
||||
// 规格
|
||||
buildTableCell(
|
||||
Text(
|
||||
itemData.product
|
||||
?.productSpecification ??
|
||||
'',
|
||||
style: textStyle),
|
||||
itemData: itemData),
|
||||
// 单价
|
||||
buildTableCell(
|
||||
Text(
|
||||
'¥${double.parse('${itemData.unitPrice}')}',
|
||||
style: textStyle,
|
||||
),
|
||||
itemData: itemData),
|
||||
itemData: itemData),
|
||||
|
||||
// 库存数量
|
||||
buildTableCell(
|
||||
Text(
|
||||
'${itemData.quantity}${itemData.product?.unit?.label ?? ''}',
|
||||
textAlign: TextAlign.right,
|
||||
style: textStyle),
|
||||
itemData: itemData,
|
||||
alignment: Alignment.centerRight),
|
||||
]);
|
||||
})
|
||||
])));
|
||||
// 库存数量
|
||||
buildTableCell(
|
||||
Text(
|
||||
'${itemData.quantity}${itemData.product?.unit?.label ?? ''}',
|
||||
textAlign: TextAlign.right,
|
||||
style: textStyle),
|
||||
itemData: itemData,
|
||||
alignment: Alignment.centerRight),
|
||||
]);
|
||||
})
|
||||
]))));
|
||||
}
|
||||
|
||||
Widget buildTableCell(Widget child,
|
||||
|
@ -421,6 +423,11 @@ class InventorySearch extends StatelessWidget {
|
|||
style: TextStyle(
|
||||
fontSize: ScreenAdaper.sp(30), color: AppTheme.grey),
|
||||
),
|
||||
Text(
|
||||
'${itemData.project?.name}',
|
||||
style: TextStyle(
|
||||
fontSize: ScreenAdaper.sp(30), color: AppTheme.grey),
|
||||
),
|
||||
Text('¥${double.parse('${itemData.unitPrice}')}',
|
||||
style: TextStyle(
|
||||
fontSize: ScreenAdaper.sp(30), color: AppTheme.grey))
|
||||
|
|
|
@ -19,6 +19,7 @@ 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';
|
||||
import 'package:sk_base_mobile/widgets/sk_appbar.dart';
|
||||
|
||||
class NewInventoryInout extends StatelessWidget {
|
||||
final NewInventoryInoutController controller;
|
||||
|
@ -32,11 +33,8 @@ class NewInventoryInout extends StatelessWidget {
|
|||
return SafeArea(
|
||||
top: false, // 设置为false以避免保留顶部状态栏的空间
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
inOrOut == InventoryInOrOutEnum.In ? '入库登记' : '出库登记',
|
||||
style: TextStyle(fontSize: ScreenAdaper.height(35)),
|
||||
),
|
||||
appBar: SkAppbar(
|
||||
title: inOrOut == InventoryInOrOutEnum.In ? '入库登记' : '出库登记',
|
||||
),
|
||||
resizeToAvoidBottomInset: true,
|
||||
body: SingleChildScrollView(
|
||||
|
|
|
@ -1,692 +0,0 @@
|
|||
// 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),
|
||||
));
|
||||
}
|
|
@ -1,754 +0,0 @@
|
|||
import 'package:data_table_2/data_table_2.dart';
|
||||
import 'package:sk_base_mobile/app_theme.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,
|
||||
),
|
||||
];
|
|
@ -1,176 +0,0 @@
|
|||
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,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
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];
|
||||
}
|
|
@ -5,7 +5,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sk_base_mobile/app_theme.dart';
|
||||
import 'package:sk_base_mobile/screens/sale_quotation/components/data_table.dart';
|
||||
import 'package:sk_base_mobile/util/modal.util.dart';
|
||||
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||
import 'package:sk_base_mobile/widgets/sk_appbar.dart';
|
||||
|
@ -44,60 +43,63 @@ class SaleQuotationPage extends StatelessWidget {
|
|||
))
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: headerBgcolor,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: AppTheme.nearlyBlack.withOpacity(0.5)))),
|
||||
padding: EdgeInsets.only(
|
||||
left: ScreenAdaper.width(20),
|
||||
top: ScreenAdaper.height(10),
|
||||
bottom: ScreenAdaper.height(10)),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
'名称',
|
||||
style: headerTitleStyle,
|
||||
),
|
||||
const Spacer(),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
width: quantityWidth,
|
||||
// constraints: BoxConstraints(minWidth: quantityWidth),
|
||||
child: Text(
|
||||
'数量',
|
||||
body: SafeArea(
|
||||
bottom: ScreenAdaper.isLandspace() ? false : true,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: headerBgcolor,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: AppTheme.nearlyBlack.withOpacity(0.5)))),
|
||||
padding: EdgeInsets.only(
|
||||
left: ScreenAdaper.width(20),
|
||||
top: ScreenAdaper.height(10),
|
||||
bottom: ScreenAdaper.height(10)),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
'名称',
|
||||
style: headerTitleStyle,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
width: unitPriceWidth,
|
||||
// constraints: BoxConstraints(minWidth: unitPriceWidth),
|
||||
child: Text(
|
||||
'单价',
|
||||
style: headerTitleStyle,
|
||||
const Spacer(),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
width: quantityWidth,
|
||||
// constraints: BoxConstraints(minWidth: quantityWidth),
|
||||
child: Text(
|
||||
'数量',
|
||||
style: headerTitleStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
width: amountWidth,
|
||||
child: Text(
|
||||
'总价',
|
||||
style: headerTitleStyle,
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
width: unitPriceWidth,
|
||||
// constraints: BoxConstraints(minWidth: unitPriceWidth),
|
||||
child: Text(
|
||||
'单价',
|
||||
style: headerTitleStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
Expanded(
|
||||
child: Obx(() => CustomScrollView(
|
||||
slivers: controller.groups
|
||||
.mapIndexed<Widget>((index, e) => buildBody(e, index))
|
||||
.toList(),
|
||||
))),
|
||||
],
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
width: amountWidth,
|
||||
child: Text(
|
||||
'总价',
|
||||
style: headerTitleStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
Expanded(
|
||||
child: Obx(() => CustomScrollView(
|
||||
slivers: controller.groups
|
||||
.mapIndexed<Widget>((index, e) => buildBody(e, index))
|
||||
.toList(),
|
||||
))),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:sk_base_mobile/util/util.dart';
|
||||
|
||||
class SkAppbar extends StatelessWidget implements PreferredSizeWidget {
|
||||
final String title;
|
||||
|
@ -8,7 +9,10 @@ class SkAppbar extends StatelessWidget implements PreferredSizeWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBar(
|
||||
title: Text(title),
|
||||
title: Text(
|
||||
title,
|
||||
style: TextStyle(fontSize: ScreenAdaper.sp(40)),
|
||||
),
|
||||
actions: action,
|
||||
);
|
||||
}
|
||||
|
|
366
pubspec.lock
366
pubspec.lock
File diff suppressed because it is too large
Load Diff
|
@ -63,7 +63,6 @@ dependencies:
|
|||
flutter_native_splash: ^2.3.11
|
||||
flutter_svg: ^2.0.10+1
|
||||
flutter_sticky_header: ^0.6.5
|
||||
data_table_2: ^2.5.12
|
||||
flutter_slidable: ^3.1.0
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
Loading…
Reference in New Issue