What can replace FocusTrap in flutter 3.7.0? - flutter

I updated flutter to 3.7.0 and found that the FocusTrap widget has been removed.
What can I replace it with? for example source from pinput library:
return _PinputFormField(
enabled: isEnabled,
validator: _validator,
child: FocusTrap(
focusNode: effectiveFocusNode,
child: MouseRegion(
cursor: _effectiveMouseCursor,
onEnter: (PointerEnterEvent event) => _handleHover(true),
onExit: (PointerExitEvent event) => _handleHover(false),
child: IgnorePointer(
ignoring: !isEnabled || !widget.useNativeKeyboard,
child: AnimatedBuilder(
animation: _effectiveController,
builder: (_, Widget? child) => Semantics(
maxValueLength: widget.length,
currentValueLength: _currentLength,
onTap: widget.readOnly ? null : _semanticsOnTap,
onDidGainAccessibilityFocus: handleDidGainAccessibilityFocus,
child: child,
),
child: _gestureDetectorBuilder.buildGestureDetector(
behavior: HitTestBehavior.translucent,
child: Stack(
alignment: Alignment.topCenter,
children: [
_buildEditable(textSelectionControls),
_buildFields(),
],
),
),
),
),
),
),
);

The offical replacement is TapRegionSurface(flutter.dev) and TapRegion
See https://github.com/flutter/flutter/pull/107262

Related

Bloc provider above OverlayEntry flutter

I am having some problems with my flutter app. I am trying to add an overlay like this in the photo below:
And it works just fine, I am able to open it on long press and close it on tap everywhere else on the screen.
The problem is that those two buttons - delete and edit - should call a bloc method that then do all the logic, but I do not have a bloc provider above the OverlayEntry. This is the error:
Error: Could not find the correct Provider<BrowseBloc> above this _OverlayEntryWidget Widget
This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- You added a new provider in your `main.dart` and performed a hot-reload.
To fix, perform a hot-restart.
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
Make sure that _OverlayEntryWidget is under your MultiProvider/Provider<BrowseBloc>.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
```
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>().toString()),
);
}
```
consider using `builder` like so:
```
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context, child) {
// No longer throws
return Text(context.watch<Example>().toString());
}
);
}
```
If none of these solutions work, consider asking for help on StackOverflow:
https://stackoverflow.com/questions/tagged/flutter
I've already encountered this error but this time I'm in a bit of trouble because I'm working with an overlay and not a widget.
This is my code:
late OverlayEntry _popupDialog;
class ExpenseCard extends StatelessWidget {
const ExpenseCard({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocConsumer<AppBloc, AppState>(
listener: (context, state) {},
buildWhen: (previous, current) => previous.theme != current.theme,
builder: (context, state) {
return Column(
children: [
GestureDetector(
onLongPress: () {
_popupDialog = _createOverlay(expense);
Overlay.of(context)?.insert(_popupDialog);
},
child: Card(
...some widgets
),
),
const Divider(height: 0),
],
);
},
);
}
}
OverlayEntry _createOverlay(Expenses e) {
return OverlayEntry(
builder: (context) => GestureDetector(
onTap: () => _popupDialog.remove(),
child: AnimatedDialog(
child: _createPopupContent(context, e),
),
),
);
}
Widget _createPopupContent(BuildContext context, Expenses e) {
return GestureDetector(
onTap: () {},
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: MediaQuery.of(context).size.width * 0.9,
decoration: BoxDecoration(
color: LocalCache.getActiveTheme() == ThemeMode.dark ? darkColorScheme.surface : lightColorScheme.surface,
borderRadius: const BorderRadius.all(Radius.circular(16)),
),
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...some other widgets
],
),
),
SizedBox(
width: 256,
child: Card(
child: Column(
children: [
InkWell(
onTap: () {
_popupDialog.remove();
// This is where the error is been thrown
context.read<BrowseBloc>().add(SetTransactionToEdit(e));
showBottomModalSheet(
context,
dateExpense: e.dateExpense,
total: e.total,
transactionToEdit: e,
);
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: Row(
children: [Text(AppLocalizations.of(context).edit), const Spacer(), const Icon(Icons.edit)],
),
),
),
const Divider(height: 0),
InkWell(
onTap: () {
_popupDialog.remove();
// This is where the error is been thrown
context.read<BrowseBloc>().add(DeleteExpense(e.id!, e.isExpense));
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: Row(
children: [Text(AppLocalizations.of(context).delete), const Spacer(), const Icon(Unicons.delete)],
),
),
),
],
),
),
),
],
),
);
}
How can I add the bloc provider above my OverlayEntry? Is this the best course of action?
Thank you to everyone that can help!
Wrap your widget that you use in OverlayEntry in BlocProvider.value constructor and pass the needed bloc as an argument to it, like so
OverlayEntry _createOverlay(Expenses e, ExampleBloc exampleBloc) {
return OverlayEntry(
builder: (context) => GestureDetector(
onTap: () => _popupDialog.remove(),
child: BlocProvider<ExampleBloc>.value(
value: exampleBloc,
child: AnimatedDialog(
child: _createPopupContent(context, e),
),
),
),
);
}
I have found a solution starting from the answer of Olga P, but changing one thing. I use the BlocProvider.value but I am passing as an argument to the method the context and not the bloc itself. This is the code:
OverlayEntry _createOverlay(Expenses e, BuildContext context) {
return OverlayEntry(
builder: (_) => GestureDetector(
onTap: () => _popupDialog.remove(),
child: BlocProvider<BrowseBloc>.value(
value: BlocProvider.of(context),
child: AnimatedDialog(
child: _createPopupContent(context, e),
),
),
),
);
}
With this change the two methods - edit and delete - work perfectly. Thanks to everyone who replied, I learned something today too!
The problem is that you are using a function and not a widget. So you can either modify _createOverlay to be stateless or stateful widget, or you can pass the bloc as an argument to the function.
In the latter case this would be _createOverlay(expense, context.read<AppBloc>())

flutter_fortune_wheel spins on page load | Flutter

I am using flutter_fortune_wheel: ^1.2.0 from pub.dev
https://pub.dev/packages/flutter_fortune_wheel
I have the Problem when i open the Page the wheel spins automatically. I want it to only spin when i Click it.
return Scaffold(
body: GestureDetector(
onHorizontalDragEnd: (DragEndDetails details) {
setState(() {
selected.add(
Random().nextInt(tricklist.length),
);
});
},
onVerticalDragEnd: (DragEndDetails details) {
setState(() {
selected.add(
Random().nextInt(tricklist.length),
);
});
},
onTap: () {
setState(() {
selected.add(
Random().nextInt(tricklist.length),
);
});
},
child: Stack(
//fit: StackFit.expand,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Align(
alignment: Alignment.center,
child: FortuneWheel(
items: [
for (var items in tricklist)
FortuneItem(
child: Text(items),
),
],
indicators: const <FortuneIndicator>[
FortuneIndicator(
alignment: Alignment.topCenter,
child: TriangleIndicator(color: Colors.black)),
],
selected: selected.stream,
),
),
),
],
),
),
);
Thx for your help
Make the animateFirst flag to false like so:
animateFirst: false
and use the onFling callback to add the behavior you want when the touches the wheel.

obx does not responsive GetX

I am using GetX. I need when the user delete the image from ui, the card delete. But nothing deleted from UI. if i pressed hot restart it deleted from the UI.
The Code Is:
class SelectedImagesWidget extends GetView<AddProductController> {
#override
Widget build(BuildContext context) {
return Obx(
() => (controller.product.value.pickedImages.isEmpty)
? const SizedBox.shrink()
: SingleChildScrollView(
clipBehavior: Clip.none,
child: SizedBox(
child:ListView.builder(
itemCount: controller.product.value.pickedImages
.length,
itemBuilder: (context, index) {
return Obx(()=>
Stack(
clipBehavior: Clip.none,
children: [
// Image
Card(
clipBehavior: Clip.hardEdge,
child: // AssetThumb(
Image.file(
controller.product.value.pickedImages
.getOrCrash()[index],
),
),
// Delete Button
Positioned(
child: InkWell(
onTap: () async =>
await controller.deleteImage(index),
child: const CircleAvatar(
child: Icon(
Icons.delete_outlined,
),
),
),
);
}
}
you can use this for refresh list
controller.product.refresh()
you can use update() function at the end of your logic function in controller

Another exception was thrown: setState() or markNeedsBuild() called during build Error in flutter

Im new to flutter and working on an ecommerce flutter app. When im trying to navigate to search screen its giving some error. Please find the below codes for your reference and help to resolve.
Error :
The following assertion was thrown while dispatching notifications for SearchProvider:
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope<SearchProvider?> widget cannot be marked as needing to build because
the framework is already in the process of building widgets. A widget can be marked as needing to
be built during the build phase only if one of its ancestors is currently building. This exception
is allowed because the framework builds parent widgets before children, which means a dirty
descendant will always be built. Otherwise, the framework might not visit this widget during this
build phase.
The widget on which setState() or markNeedsBuild() was called was:
_InheritedProviderScope<SearchProvider?>
The widget which was currently being built when the offending call was made was:
SearchScreen
Codes
Search Screen
class SearchScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
Provider.of<SearchProvider>(context, listen: false).cleanSearchProduct();
Provider.of<SearchProvider>(context, listen: false).initHistoryList();
return Scaffold(
backgroundColor: ColorResources.getIconBg(context),
resizeToAvoidBottomInset: true,
body: Column(
children: [
// for tool bar
SearchWidget(
hintText: getTranslated('SEARCH_HINT', context),
onSubmit: (String text) {
Provider.of<SearchProvider>(context, listen: false)
.searchProduct(text, context);
Provider.of<SearchProvider>(context, listen: false)
.saveSearchAddress(text);
},
onClearPressed: () {
Provider.of<SearchProvider>(context, listen: false)
.cleanSearchProduct();
},
),
Consumer<SearchProvider>(
builder: (context, searchProvider, child) {
return !searchProvider.isClear
? searchProvider.searchProductList != null
? searchProvider.searchProductList.length > 0
? Expanded(
child: SearchProductWidget(
products: searchProvider.searchProductList,
isViewScrollable: true))
: Expanded(
child:
NoInternetOrDataScreen(isNoInternet: false))
: Expanded(
child: ProductShimmer(
isHomePage: false,
isEnabled: Provider.of<SearchProvider>(context)
.searchProductList ==
null))
: Expanded(
flex: 4,
child: Container(
padding:
EdgeInsets.all(Dimensions.PADDING_SIZE_DEFAULT),
child: Stack(
clipBehavior: Clip.none,
children: [
Consumer<SearchProvider>(
builder: (context, searchProvider, child) =>
StaggeredGridView.countBuilder(
crossAxisCount: 3,
physics: NeverScrollableScrollPhysics(),
itemCount: searchProvider.historyList.length,
itemBuilder: (context, index) => Container(
alignment: Alignment.center,
child: InkWell(
onTap: () {
Provider.of<SearchProvider>(context,
listen: false)
.searchProduct(
searchProvider
.historyList[index],
context);
},
borderRadius: BorderRadius.circular(20),
child: Container(
padding: EdgeInsets.only(
left: 10,
right: 10,
top: 2,
bottom: 2),
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(16),
color: ColorResources.getGrey(
context)),
width: double.infinity,
child: Center(
child: Text(
Provider.of<SearchProvider>(context,
listen: false)
.historyList[index] ??
"",
style: titilliumItalic.copyWith(
fontSize:
Dimensions.FONT_SIZE_SMALL),
),
),
),
)),
staggeredTileBuilder: (int index) =>
new StaggeredTile.fit(1),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
),
),
Positioned(
top: -5,
left: 0,
right: 0,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(getTranslated('SEARCH_HISTORY', context),
style: robotoBold),
InkWell(
borderRadius: BorderRadius.circular(10),
onTap: () {
Provider.of<SearchProvider>(context,
listen: false)
.clearSearchAddress();
},
child: Container(
padding: EdgeInsets.all(5),
child: Text(
getTranslated('REMOVE', context),
style: titilliumRegular.copyWith(
fontSize:
Dimensions.FONT_SIZE_SMALL,
color: Theme.of(context)
.primaryColor),
)))
],
),
),
],
),
),
);
},
),
],
),
);
}
}
Providers
void initHistoryList() {
_historyList = [];
_historyList.addAll(searchRepo.getSearchAddress());
notifyListeners();
}
void cleanSearchProduct() {
_searchProductList = [];
_isClear = true;
_searchText = '';
notifyListeners();
}
Try to use initial function calling in initState instead of build function
#override
void initState() {
WidgetsBinding.instance!.addPostFrameCallback((_) {
Provider.of<SearchProvider>(context, listen: false).cleanSearchProduct();
Provider.of<SearchProvider>(context, listen: false).initHistoryList();
});
super.initState();
}

conditional rendering List with flutter

I have a model of 'contracts' and one of the parameters is if the contract is active/inactive. I did this condition using a bool true/false in the contractModel file.
Now, I am rendering a ListView.Builder with all the contracts, but I have created a filter at the top of the screen to select the contracts that are active/inactive. What I want to achieve is to render the active contracts in the 'active' ListView.Builder and the inactive contracts in the 'inactive' ListView.Builder.
This is the code for the ListView.Builder:
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Categories(),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: contracts.length,
itemBuilder: (context, index) => ContractCard(
contract: contracts[index],
press: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailsScreen(
contract: contracts[index],
),
),
),
),
),
),
),
],
);
}
}
Any help is appreciated.
Reference package(flutter package), explanation from the author (medium post).This is how you would normally conditionally render widgets
return Column(
children: <Widget>[
someCondition == true ?
Text('The condition is true!'):
Text('The condition is false!'),
],
);
with the above package though you would instead do
return Column(
children: <Widget>[
Conditional.single(
context: context,
conditionBuilder: (BuildContext context) => someCondition == true,
widgetBuilder: (BuildContext context) => Text('The condition is true!'),
fallbackBuilder: (BuildContext context) => Text('The condition is false!'),
),
],
);