如何使用 BLoC 以编程方式关闭 CupertinoActionSheet?
How to close the CupertinoActionSheet programatically with BLoC?
我正在构建一个扫描二维码的 2FA 应用程序。然后它解析 QR 码中的 uri 和 returns 一个 totp 代码。但是,我在使用 Navigator.of(context).pop().
关闭 CupertinoActionSheet 时遇到问题
我希望它像那样工作:
- 当用户点击“添加”按钮时,会显示操作 Sheet。
- 当用户点击“扫描二维码”时,操作 Sheet 必须关闭然后继续扫描。
它是这样的:
这是当我有以下代码时出现的错误:“查找已停用的小部件的祖先是不安全的。
此时小部件的元素树的状态不再稳定。
要在其 dispose() 方法中安全地引用小部件的祖先,请通过在小部件的 didChangeDependencies() 方法中调用 dependOnInheritedWidgetOfExactType() 来保存对祖先的引用。"
代码:
home_screen.dart
import 'dart:io';
import 'package:duckie/blocs/manual_input/manual_input_bloc.dart';
import 'package:duckie/blocs/qr_code_scanner/qr_code_scanner_bloc.dart';
import 'package:duckie/screens/manual_input/manual_input_screen.dart';
import 'package:duckie/screens/widgets/alert_dialog.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:duckie/shared/text_styles.dart';
import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'title',
style: TextStyles.appBarText,
).tr(),
centerTitle: false,
elevation: 0.0,
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: () {
if (Platform.isAndroid) {
showAndroidModalBottomSheet(context);
} else if (Platform.isIOS) {
showIosActionSheet(context);
}
},
),
],
),
body: BlocConsumer<QrCodeScannerBloc, QrCodeScannerState>(
listener: (context, state) {
if (state is QrCodeScannerError) {
Platform.isAndroid
? CustomAlertDialog.showAndroidAlertDialog(context,
state.alertDialogErrorTitle, state.alertDialogErrorContent)
: CustomAlertDialog.showIosAlertDialog(context,
state.alertDialogErrorTitle, state.alertDialogErrorContent);
}
},
builder: (context, state) {
if (state is QrCodeScannerFinal) {
return Column(
children: [
Text(state.accountName),
Text(state.issuer),
Text(state.otp),
],
);
}
return Container();
},
),
);
}
}
void showAndroidModalBottomSheet(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return ListView(
shrinkWrap: true,
children: [
ListTile(
onTap: () {},
leading: Icon(Icons.qr_code),
title: Text('scan-qr-code').tr(),
),
ListTile(
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).pushNamed('/manual-input');
},
leading: Icon(Icons.keyboard),
title: Text('manual-input').tr(),
),
ListTile(
onTap: () {
Navigator.of(context).pop();
},
leading: Icon(Icons.cancel),
title: Text('cancel').tr(),
)
],
);
},
);
}
void showIosActionSheet(BuildContext context) {
showCupertinoModalPopup(
context: context,
builder: (BuildContext context) {
return CupertinoActionSheet(
title: Text('action-sheet-title').tr(),
message: Text('action-sheet-message').tr(),
actions: [
CupertinoActionSheetAction(
onPressed: () async {
Navigator.pop(context);
final String qrCodeResponse =
await FlutterBarcodeScanner.scanBarcode(
'#FF6666', 'cancel'.tr(), true, ScanMode.QR);
BlocProvider.of<QrCodeScannerBloc>(context).add(
GetQrCodeResponseEvent(qrCodeResponse),
);
// final String uri =
// 'otpauth://totp/Karol%27s%20Nextcloud%3Aszakes1%40drive.karolzientek.tech?secret=KVZKSQ74SSAVCR27&issuer=Karol%27s%20Nextcloud';
},
child: Text('scan-qr-code').tr(),
),
CupertinoActionSheetAction(
onPressed: () {
Navigator.of(context).pop();
Navigator.of(context).pushNamed('/manual-input');
},
child: Text('manual-input').tr(),
),
CupertinoActionSheetAction(
isDestructiveAction: true,
onPressed: () {
Navigator.of(context).pop();
},
child: Text('cancel').tr(),
)
],
);
},
);
}
我认为问题出在这段代码中:
onPressed: () async {
Navigator.pop(context);
final String qrCodeResponse =
await FlutterBarcodeScanner.scanBarcode(
'#FF6666', 'cancel'.tr(), true, ScanMode.QR);
BlocProvider.of<QrCodeScannerBloc>(context).add(
GetQrCodeResponseEvent(qrCodeResponse),
);
…
},
你正在做的是:
- 从导航堆栈中弹出 CupertinoActionSheet。
- 做一些异步计算。
- 使用
context
访问QrCodeScannerBloc。
但是,在 1 之后,context
(== 支持 CupertinoActionSheet 的元素)不再处于活动状态 - 它已被处理掉。以后无法使用它,因为它与树分离。
要避免这种情况,您可以先存储对 QrCodeScannerBloc 的引用,然后再使用它,如下所示:
onPressed: () async {
final qrCodeScannerBloc = BlocProvider.of<QrCodeScannerBloc>(context);
Navigator.pop(context);
final String qrCodeResponse =
await FlutterBarcodeScanner.scanBarcode(
'#FF6666', 'cancel'.tr(), true, ScanMode.QR);
qrCodeScannerBloc.add(GetQrCodeResponseEvent(qrCodeResponse));
…
},
我正在构建一个扫描二维码的 2FA 应用程序。然后它解析 QR 码中的 uri 和 returns 一个 totp 代码。但是,我在使用 Navigator.of(context).pop().
关闭 CupertinoActionSheet 时遇到问题我希望它像那样工作:
- 当用户点击“添加”按钮时,会显示操作 Sheet。
- 当用户点击“扫描二维码”时,操作 Sheet 必须关闭然后继续扫描。
它是这样的:
这是当我有以下代码时出现的错误:“查找已停用的小部件的祖先是不安全的。 此时小部件的元素树的状态不再稳定。 要在其 dispose() 方法中安全地引用小部件的祖先,请通过在小部件的 didChangeDependencies() 方法中调用 dependOnInheritedWidgetOfExactType() 来保存对祖先的引用。"
代码:
home_screen.dart
import 'dart:io';
import 'package:duckie/blocs/manual_input/manual_input_bloc.dart';
import 'package:duckie/blocs/qr_code_scanner/qr_code_scanner_bloc.dart';
import 'package:duckie/screens/manual_input/manual_input_screen.dart';
import 'package:duckie/screens/widgets/alert_dialog.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:duckie/shared/text_styles.dart';
import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'title',
style: TextStyles.appBarText,
).tr(),
centerTitle: false,
elevation: 0.0,
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: () {
if (Platform.isAndroid) {
showAndroidModalBottomSheet(context);
} else if (Platform.isIOS) {
showIosActionSheet(context);
}
},
),
],
),
body: BlocConsumer<QrCodeScannerBloc, QrCodeScannerState>(
listener: (context, state) {
if (state is QrCodeScannerError) {
Platform.isAndroid
? CustomAlertDialog.showAndroidAlertDialog(context,
state.alertDialogErrorTitle, state.alertDialogErrorContent)
: CustomAlertDialog.showIosAlertDialog(context,
state.alertDialogErrorTitle, state.alertDialogErrorContent);
}
},
builder: (context, state) {
if (state is QrCodeScannerFinal) {
return Column(
children: [
Text(state.accountName),
Text(state.issuer),
Text(state.otp),
],
);
}
return Container();
},
),
);
}
}
void showAndroidModalBottomSheet(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return ListView(
shrinkWrap: true,
children: [
ListTile(
onTap: () {},
leading: Icon(Icons.qr_code),
title: Text('scan-qr-code').tr(),
),
ListTile(
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).pushNamed('/manual-input');
},
leading: Icon(Icons.keyboard),
title: Text('manual-input').tr(),
),
ListTile(
onTap: () {
Navigator.of(context).pop();
},
leading: Icon(Icons.cancel),
title: Text('cancel').tr(),
)
],
);
},
);
}
void showIosActionSheet(BuildContext context) {
showCupertinoModalPopup(
context: context,
builder: (BuildContext context) {
return CupertinoActionSheet(
title: Text('action-sheet-title').tr(),
message: Text('action-sheet-message').tr(),
actions: [
CupertinoActionSheetAction(
onPressed: () async {
Navigator.pop(context);
final String qrCodeResponse =
await FlutterBarcodeScanner.scanBarcode(
'#FF6666', 'cancel'.tr(), true, ScanMode.QR);
BlocProvider.of<QrCodeScannerBloc>(context).add(
GetQrCodeResponseEvent(qrCodeResponse),
);
// final String uri =
// 'otpauth://totp/Karol%27s%20Nextcloud%3Aszakes1%40drive.karolzientek.tech?secret=KVZKSQ74SSAVCR27&issuer=Karol%27s%20Nextcloud';
},
child: Text('scan-qr-code').tr(),
),
CupertinoActionSheetAction(
onPressed: () {
Navigator.of(context).pop();
Navigator.of(context).pushNamed('/manual-input');
},
child: Text('manual-input').tr(),
),
CupertinoActionSheetAction(
isDestructiveAction: true,
onPressed: () {
Navigator.of(context).pop();
},
child: Text('cancel').tr(),
)
],
);
},
);
}
我认为问题出在这段代码中:
onPressed: () async {
Navigator.pop(context);
final String qrCodeResponse =
await FlutterBarcodeScanner.scanBarcode(
'#FF6666', 'cancel'.tr(), true, ScanMode.QR);
BlocProvider.of<QrCodeScannerBloc>(context).add(
GetQrCodeResponseEvent(qrCodeResponse),
);
…
},
你正在做的是:
- 从导航堆栈中弹出 CupertinoActionSheet。
- 做一些异步计算。
- 使用
context
访问QrCodeScannerBloc。
但是,在 1 之后,context
(== 支持 CupertinoActionSheet 的元素)不再处于活动状态 - 它已被处理掉。以后无法使用它,因为它与树分离。
要避免这种情况,您可以先存储对 QrCodeScannerBloc 的引用,然后再使用它,如下所示:
onPressed: () async {
final qrCodeScannerBloc = BlocProvider.of<QrCodeScannerBloc>(context);
Navigator.pop(context);
final String qrCodeResponse =
await FlutterBarcodeScanner.scanBarcode(
'#FF6666', 'cancel'.tr(), true, ScanMode.QR);
qrCodeScannerBloc.add(GetQrCodeResponseEvent(qrCodeResponse));
…
},