refactor(client): 只读判断统一走 WriteGuard 控件
将各屏内联的 ref.watch(isReadonlyProvider) 只读判断统一改为 WriteGuard(child:) 或 WriteGuard.isReadonly(ref),全项目只读逻辑 单一入口(仅 write_guard.dart 内部保留 provider 读取)。行为不变。 121 测试通过,analyze 无 error。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ import '../../widgets/page_scaffold.dart';
|
|||||||
import '../../providers/connectivity_provider.dart';
|
import '../../providers/connectivity_provider.dart';
|
||||||
import '../../core/utils/export_util.dart';
|
import '../../core/utils/export_util.dart';
|
||||||
import '../../repositories/finance_repository.dart';
|
import '../../repositories/finance_repository.dart';
|
||||||
import '../../core/auth/auth_state.dart';
|
import '../../widgets/write_guard.dart';
|
||||||
|
|
||||||
class FinanceScreen extends ConsumerWidget {
|
class FinanceScreen extends ConsumerWidget {
|
||||||
const FinanceScreen({super.key});
|
const FinanceScreen({super.key});
|
||||||
@@ -161,7 +161,7 @@ class _FinanceTabState extends ConsumerState<_FinanceTab> {
|
|||||||
MobileCardField('状态', null, valueWidget: _StatusBadge(r.status)),
|
MobileCardField('状态', null, valueWidget: _StatusBadge(r.status)),
|
||||||
if (r.remark?.isNotEmpty == true) MobileCardField('备注', r.remark),
|
if (r.remark?.isNotEmpty == true) MobileCardField('备注', r.remark),
|
||||||
],
|
],
|
||||||
actions: (canClose && !ref.watch(isReadonlyProvider))
|
actions: (canClose && !WriteGuard.isReadonly(ref))
|
||||||
? [
|
? [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => _closeRecord(r),
|
onPressed: () => _closeRecord(r),
|
||||||
@@ -307,7 +307,7 @@ class _FinanceTabState extends ConsumerState<_FinanceTab> {
|
|||||||
case 'actions':
|
case 'actions':
|
||||||
if ((r.type == 'payable' || r.type == 'receivable') &&
|
if ((r.type == 'payable' || r.type == 'receivable') &&
|
||||||
r.status == 'open' &&
|
r.status == 'open' &&
|
||||||
!ref.watch(isReadonlyProvider)) {
|
!WriteGuard.isReadonly(ref)) {
|
||||||
return DataCell(TextButton(
|
return DataCell(TextButton(
|
||||||
onPressed: () => _closeRecord(r),
|
onPressed: () => _closeRecord(r),
|
||||||
child: const Text('结清',
|
child: const Text('结清',
|
||||||
@@ -408,13 +408,13 @@ class _FinanceTabState extends ConsumerState<_FinanceTab> {
|
|||||||
},
|
},
|
||||||
toolbar: Row(
|
toolbar: Row(
|
||||||
children: [
|
children: [
|
||||||
if (addLabel != null && !ref.watch(isReadonlyProvider))
|
if (addLabel != null && !WriteGuard.isReadonly(ref))
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
onPressed: _showAddDialog,
|
onPressed: _showAddDialog,
|
||||||
icon: const Icon(Icons.add, size: 16),
|
icon: const Icon(Icons.add, size: 16),
|
||||||
label: Text(addLabel),
|
label: Text(addLabel),
|
||||||
),
|
),
|
||||||
if (addLabel != null && !ref.watch(isReadonlyProvider))
|
if (addLabel != null && !WriteGuard.isReadonly(ref))
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
OutlinedButton.icon(
|
OutlinedButton.icon(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import '../../core/responsive/responsive.dart';
|
|||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
import '../../models/inventory.dart';
|
import '../../models/inventory.dart';
|
||||||
import '../../core/auth/auth_state.dart';
|
import '../../core/auth/auth_state.dart';
|
||||||
|
import '../../widgets/write_guard.dart';
|
||||||
import '../../providers/inventory_provider.dart';
|
import '../../providers/inventory_provider.dart';
|
||||||
import '../../widgets/data_table_card.dart';
|
import '../../widgets/data_table_card.dart';
|
||||||
import '../../widgets/mobile_list_card.dart';
|
import '../../widgets/mobile_list_card.dart';
|
||||||
@@ -292,7 +293,7 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
|||||||
message: item.remark.isEmpty ? '' : item.remark,
|
message: item.remark.isEmpty ? '' : item.remark,
|
||||||
waitDuration: const Duration(milliseconds: 300),
|
waitDuration: const Duration(milliseconds: 300),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: ref.watch(isReadonlyProvider)
|
onTap: WriteGuard.isReadonly(ref)
|
||||||
? null
|
? null
|
||||||
: () => _editRemark(context, item),
|
: () => _editRemark(context, item),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -360,7 +361,7 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
|||||||
MobileCardField('供应商', item.supplierName),
|
MobileCardField('供应商', item.supplierName),
|
||||||
],
|
],
|
||||||
actions: [
|
actions: [
|
||||||
if (!ref.watch(isReadonlyProvider))
|
if (!WriteGuard.isReadonly(ref))
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => _editRemark(context, item),
|
onPressed: () => _editRemark(context, item),
|
||||||
child: const Text('备注', style: TextStyle(fontSize: 13)),
|
child: const Text('备注', style: TextStyle(fontSize: 13)),
|
||||||
@@ -554,7 +555,7 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
|||||||
ref.read(inventoryListProvider.notifier).setPageSize(s),
|
ref.read(inventoryListProvider.notifier).setPageSize(s),
|
||||||
toolbar: Row(
|
toolbar: Row(
|
||||||
children: [
|
children: [
|
||||||
if (!ref.watch(isReadonlyProvider)) ...[
|
if (!WriteGuard.isReadonly(ref)) ...[
|
||||||
OutlinedButton.icon(
|
OutlinedButton.icon(
|
||||||
onPressed: () => context.go('/inventory/check'),
|
onPressed: () => context.go('/inventory/check'),
|
||||||
icon: const Icon(Icons.fact_check, size: 16),
|
icon: const Icon(Icons.fact_check, size: 16),
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import '../../widgets/data_table_card.dart';
|
|||||||
import '../../widgets/mobile_list_card.dart';
|
import '../../widgets/mobile_list_card.dart';
|
||||||
import '../../widgets/page_scaffold.dart';
|
import '../../widgets/page_scaffold.dart';
|
||||||
import '../../core/utils/export_util.dart';
|
import '../../core/utils/export_util.dart';
|
||||||
import '../../core/auth/auth_state.dart';
|
import '../../widgets/write_guard.dart';
|
||||||
|
|
||||||
class PartnersScreen extends ConsumerStatefulWidget {
|
class PartnersScreen extends ConsumerStatefulWidget {
|
||||||
const PartnersScreen({super.key});
|
const PartnersScreen({super.key});
|
||||||
@@ -167,7 +167,7 @@ class _PartnersScreenState extends ConsumerState<PartnersScreen> {
|
|||||||
if (p.phone?.isNotEmpty == true) MobileCardField('电话', p.phone),
|
if (p.phone?.isNotEmpty == true) MobileCardField('电话', p.phone),
|
||||||
if (p.address?.isNotEmpty == true) MobileCardField('地址', p.address),
|
if (p.address?.isNotEmpty == true) MobileCardField('地址', p.address),
|
||||||
],
|
],
|
||||||
actions: ref.watch(isReadonlyProvider)
|
actions: WriteGuard.isReadonly(ref)
|
||||||
? const []
|
? const []
|
||||||
: [
|
: [
|
||||||
TextButton(
|
TextButton(
|
||||||
@@ -194,7 +194,7 @@ class _PartnersScreenState extends ConsumerState<PartnersScreen> {
|
|||||||
mobileCards: partners.map(partnerCard).toList(),
|
mobileCards: partners.map(partnerCard).toList(),
|
||||||
toolbar: Row(
|
toolbar: Row(
|
||||||
children: [
|
children: [
|
||||||
if (!ref.watch(isReadonlyProvider)) ...[
|
if (!WriteGuard.isReadonly(ref)) ...[
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
onPressed: onAdd,
|
onPressed: onAdd,
|
||||||
icon: const Icon(Icons.add, size: 16),
|
icon: const Icon(Icons.add, size: 16),
|
||||||
@@ -285,7 +285,7 @@ class _PartnersScreenState extends ConsumerState<PartnersScreen> {
|
|||||||
)),
|
)),
|
||||||
DataCell(Row(
|
DataCell(Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: ref.watch(isReadonlyProvider)
|
children: WriteGuard.isReadonly(ref)
|
||||||
? const []
|
? const []
|
||||||
: [
|
: [
|
||||||
TextButton(
|
TextButton(
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import '../../widgets/data_table_card.dart';
|
|||||||
import '../../widgets/mobile_list_card.dart';
|
import '../../widgets/mobile_list_card.dart';
|
||||||
import '../../widgets/page_scaffold.dart';
|
import '../../widgets/page_scaffold.dart';
|
||||||
import '../../core/utils/export_util.dart';
|
import '../../core/utils/export_util.dart';
|
||||||
import '../../core/auth/auth_state.dart';
|
import '../../widgets/write_guard.dart';
|
||||||
|
|
||||||
class ProductsScreen extends ConsumerStatefulWidget {
|
class ProductsScreen extends ConsumerStatefulWidget {
|
||||||
const ProductsScreen({super.key});
|
const ProductsScreen({super.key});
|
||||||
@@ -355,10 +355,9 @@ class _ProductsScreenState extends ConsumerState<ProductsScreen> {
|
|||||||
required VoidCallback onAdd,
|
required VoidCallback onAdd,
|
||||||
required VoidCallback onExport,
|
required VoidCallback onExport,
|
||||||
}) {
|
}) {
|
||||||
final readonly = ref.watch(isReadonlyProvider);
|
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
if (!readonly) ...[
|
if (!WriteGuard.isReadonly(ref)) ...[
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
onPressed: onAdd,
|
onPressed: onAdd,
|
||||||
icon: const Icon(Icons.add, size: 16),
|
icon: const Icon(Icons.add, size: 16),
|
||||||
@@ -420,7 +419,7 @@ class _ProductsScreenState extends ConsumerState<ProductsScreen> {
|
|||||||
MobileCardField('单品数量', '$quantity'),
|
MobileCardField('单品数量', '$quantity'),
|
||||||
if (remark?.isNotEmpty == true) MobileCardField('备注', remark),
|
if (remark?.isNotEmpty == true) MobileCardField('备注', remark),
|
||||||
],
|
],
|
||||||
actions: ref.watch(isReadonlyProvider)
|
actions: WriteGuard.isReadonly(ref)
|
||||||
? const []
|
? const []
|
||||||
: [
|
: [
|
||||||
TextButton(
|
TextButton(
|
||||||
@@ -437,19 +436,20 @@ class _ProductsScreenState extends ConsumerState<ProductsScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _actionButtons({required VoidCallback onEdit, required VoidCallback onDelete}) {
|
Widget _actionButtons({required VoidCallback onEdit, required VoidCallback onDelete}) {
|
||||||
if (ref.watch(isReadonlyProvider)) return const SizedBox.shrink();
|
return WriteGuard(
|
||||||
return Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: onEdit,
|
onPressed: onEdit,
|
||||||
child: const Text('编辑', style: TextStyle(fontSize: 12)),
|
child: const Text('编辑', style: TextStyle(fontSize: 12)),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: onDelete,
|
onPressed: onDelete,
|
||||||
child: const Text('删除', style: TextStyle(fontSize: 12, color: AppTheme.danger)),
|
child: const Text('删除', style: TextStyle(fontSize: 12, color: AppTheme.danger)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import '../../core/auth/auth_state.dart';
|
import '../../core/auth/auth_state.dart';
|
||||||
|
import '../../widgets/write_guard.dart';
|
||||||
import '../../core/config/app_config.dart';
|
import '../../core/config/app_config.dart';
|
||||||
import '../../core/config/app_info.dart';
|
import '../../core/config/app_info.dart';
|
||||||
import '../../core/responsive/responsive.dart';
|
import '../../core/responsive/responsive.dart';
|
||||||
@@ -218,12 +219,13 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
|||||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
if (!ref.watch(isReadonlyProvider))
|
WriteGuard(
|
||||||
ElevatedButton.icon(
|
child: ElevatedButton.icon(
|
||||||
onPressed: () => _showAddUserDialog(context),
|
onPressed: () => _showAddUserDialog(context),
|
||||||
icon: const Icon(Icons.person_add, size: 16),
|
icon: const Icon(Icons.person_add, size: 16),
|
||||||
label: const Text('新增用户'),
|
label: const Text('新增用户'),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -293,7 +295,7 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
|||||||
)),
|
)),
|
||||||
DataCell(Row(
|
DataCell(Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: ref.watch(isReadonlyProvider)
|
children: WriteGuard.isReadonly(ref)
|
||||||
? const []
|
? const []
|
||||||
: [
|
: [
|
||||||
TextButton(
|
TextButton(
|
||||||
@@ -457,7 +459,7 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
|||||||
style: const TextStyle(fontSize: 13),
|
style: const TextStyle(fontSize: 13),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (!ref.watch(isReadonlyProvider)) ...[
|
if (!WriteGuard.isReadonly(ref)) ...[
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: _isActivating ? null : _activateLicense,
|
onPressed: _isActivating ? null : _activateLicense,
|
||||||
@@ -619,13 +621,12 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
|||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontFamily: 'monospace', fontSize: 12))),
|
fontFamily: 'monospace', fontSize: 12))),
|
||||||
DataCell(Text('${r.currentNo}')),
|
DataCell(Text('${r.currentNo}')),
|
||||||
DataCell(ref.watch(isReadonlyProvider)
|
DataCell(WriteGuard(
|
||||||
? const SizedBox()
|
child: TextButton(
|
||||||
: TextButton(
|
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
_showNumberRuleDialog(context, r),
|
_showNumberRuleDialog(context, r),
|
||||||
child: const Text('编辑',
|
child: const Text('编辑',
|
||||||
style: TextStyle(fontSize: 12)))),
|
style: TextStyle(fontSize: 12))))),
|
||||||
]))
|
]))
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
@@ -804,7 +805,7 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
|||||||
|
|
||||||
// ── 数据导入 Tab ──────────────────────────────────────────
|
// ── 数据导入 Tab ──────────────────────────────────────────
|
||||||
Widget _buildImportTab() {
|
Widget _buildImportTab() {
|
||||||
if (ref.watch(isReadonlyProvider)) {
|
if (WriteGuard.isReadonly(ref)) {
|
||||||
return const Center(
|
return const Center(
|
||||||
child: Text('只读账号无导入权限',
|
child: Text('只读账号无导入权限',
|
||||||
style: TextStyle(color: AppTheme.textSecondary)),
|
style: TextStyle(color: AppTheme.textSecondary)),
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:intl/intl.dart';
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||||
import '../../core/auth/auth_state.dart';
|
import '../../core/auth/auth_state.dart';
|
||||||
|
import '../../widgets/write_guard.dart';
|
||||||
import '../../core/config/app_config.dart';
|
import '../../core/config/app_config.dart';
|
||||||
import '../../core/responsive/responsive.dart';
|
import '../../core/responsive/responsive.dart';
|
||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
@@ -210,7 +211,7 @@ class _AppShellState extends ConsumerState<AppShell> {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
_ShopButton(user: user, version: appVersion),
|
_ShopButton(user: user, version: appVersion),
|
||||||
if (ref.watch(isReadonlyProvider)) ...[
|
if (WriteGuard.isReadonly(ref)) ...[
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Container(
|
Container(
|
||||||
padding:
|
padding:
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import '../../providers/product_provider.dart' show productRepositoryProvider;
|
|||||||
import '../../repositories/product_repository.dart';
|
import '../../repositories/product_repository.dart';
|
||||||
import '../../providers/finance_provider.dart' show financeRepositoryProvider;
|
import '../../providers/finance_provider.dart' show financeRepositoryProvider;
|
||||||
import '../../providers/shop_provider.dart' show shopInfoProvider;
|
import '../../providers/shop_provider.dart' show shopInfoProvider;
|
||||||
import '../../core/auth/auth_state.dart';
|
import '../../widgets/write_guard.dart';
|
||||||
|
|
||||||
class StockInListScreen extends ConsumerStatefulWidget {
|
class StockInListScreen extends ConsumerStatefulWidget {
|
||||||
const StockInListScreen({super.key});
|
const StockInListScreen({super.key});
|
||||||
@@ -281,7 +281,7 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
|||||||
ref.read(stockInListProvider.notifier).setPageSize(s),
|
ref.read(stockInListProvider.notifier).setPageSize(s),
|
||||||
toolbar: Row(
|
toolbar: Row(
|
||||||
children: [
|
children: [
|
||||||
if (showNewButton && !ref.watch(isReadonlyProvider))
|
if (showNewButton && !WriteGuard.isReadonly(ref))
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
onPressed: () => context.go('/stock-in/new'),
|
onPressed: () => context.go('/stock-in/new'),
|
||||||
icon: const Icon(Icons.add, size: 16),
|
icon: const Icon(Icons.add, size: 16),
|
||||||
@@ -345,7 +345,7 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
|||||||
|
|
||||||
/// 操作按钮列表,表格与移动端卡片共用。
|
/// 操作按钮列表,表格与移动端卡片共用。
|
||||||
List<Widget> _orderActions(BuildContext context, StockInOrder o) {
|
List<Widget> _orderActions(BuildContext context, StockInOrder o) {
|
||||||
final readonly = ref.watch(isReadonlyProvider);
|
final readonly = WriteGuard.isReadonly(ref);
|
||||||
return [
|
return [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => _showDetail(context, o.id),
|
onPressed: () => _showDetail(context, o.id),
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import '../../providers/inventory_provider.dart';
|
|||||||
import '../../providers/tab_state_provider.dart';
|
import '../../providers/tab_state_provider.dart';
|
||||||
import '../../providers/product_provider.dart';
|
import '../../providers/product_provider.dart';
|
||||||
import '../../providers/finance_provider.dart' show financeRepositoryProvider;
|
import '../../providers/finance_provider.dart' show financeRepositoryProvider;
|
||||||
import '../../core/auth/auth_state.dart';
|
import '../../widgets/write_guard.dart';
|
||||||
|
|
||||||
class StockOutListScreen extends ConsumerStatefulWidget {
|
class StockOutListScreen extends ConsumerStatefulWidget {
|
||||||
const StockOutListScreen({super.key});
|
const StockOutListScreen({super.key});
|
||||||
@@ -287,7 +287,7 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
|||||||
ref.read(stockOutListProvider.notifier).setPageSize(s),
|
ref.read(stockOutListProvider.notifier).setPageSize(s),
|
||||||
toolbar: Row(
|
toolbar: Row(
|
||||||
children: [
|
children: [
|
||||||
if (showNewButton && !ref.watch(isReadonlyProvider))
|
if (showNewButton && !WriteGuard.isReadonly(ref))
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
onPressed: () => context.go('/stock-out/new'),
|
onPressed: () => context.go('/stock-out/new'),
|
||||||
icon: const Icon(Icons.add, size: 16),
|
icon: const Icon(Icons.add, size: 16),
|
||||||
@@ -351,7 +351,7 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
|||||||
|
|
||||||
/// 操作按钮列表,表格与移动端卡片共用。
|
/// 操作按钮列表,表格与移动端卡片共用。
|
||||||
List<Widget> _orderActions(BuildContext context, StockOutOrder o) {
|
List<Widget> _orderActions(BuildContext context, StockOutOrder o) {
|
||||||
final readonly = ref.watch(isReadonlyProvider);
|
final readonly = WriteGuard.isReadonly(ref);
|
||||||
return [
|
return [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => _showDetail(context, o.id),
|
onPressed: () => _showDetail(context, o.id),
|
||||||
|
|||||||
Reference in New Issue
Block a user