I cannot see my pictures in flutter swiper - flutter

I am new in flutter development. I cannot find my mistake. I can't see my images on my app. when I use them without slider it works what is wrong in my code can someone help me?
import 'package:feedme_start/widgets/Navigation_Drawer_Widget.dart';
import 'package:flutter/material.dart';
// ignore: import_of_legacy_library_into_null_safe
import 'package:flutter_swiper/flutter_swiper.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
final imageList = [
"https://cdn.yeniakit.com.tr/images/news/625/pratik-degisik-yemek-tarifleri-en-sevilen-tarifler-h1581081558-3ff37b.jpg"
"https://cdn.yemek.com/mncrop/940/625/uploads/2021/04/patlicanli-pilav-yemekcom.jpg"
];
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.red,
title: Center(child: Text("FEED ME")),
actions: <Widget>[
IconButton(onPressed: () {}, icon: Icon(Icons.call))
],
),
drawer: NavigationDrawerWidget(),
backgroundColor: Colors.white,
body:
Container(
constraints: BoxConstraints.expand(height: 200),
child: imageSlider(context),
),
/*Swiper(itemCount: imageList.length,
itemBuilder: (context, index) {
return Image.network(imageList[index],/*errorBuilder:
(BuildContext context, Object exception, StackTrace? stackTrace), {return const("resim yüklenemedi")},*/
fit: BoxFit.cover,);
},)*/
),
);
}
}
Swiper imageSlider(context){
return new Swiper(
autoplay: true,
itemBuilder: (BuildContext context, int index) {
return new Image.network("https://cdn.yeniakit.com.tr/images/news/625/pratik-degisik-yemek-tarifleri-en-sevilen-tarifler-h1581081558-3ff37b.jpg",fit: BoxFit.fitHeight,);
},
itemCount: 10,
viewportFraction: 0.8,
scale: 0.9,
);
}
also here is the screenshots;
when I run the program it tries to upload the image. then it sends me the this screen and shows the 'rethrow' line error:
after I continue debugging my screen looks like in the second picture:

You're simply forgetting to add , in your list.
A newline string after a string is considered as a string. For example, the following variable:
var text = "one two three"
"four five six";
is the same as:
var text = "one two three" + "four five six";
So, instead of:
final imageList = [
"https://cdn.yeniakit.com.tr/images/news/625/pratik-degisik-yemek-tarifleri-en-sevilen-tarifler-h1581081558-3ff37b.jpg"
"https://cdn.yemek.com/mncrop/940/625/uploads/2021/04/patlicanli-pilav-yemekcom.jpg"
];
change to:
final imageList = [
"https://cdn.yeniakit.com.tr/images/news/625/pratik-degisik-yemek-tarifleri-en-sevilen-tarifler-h1581081558-3ff37b.jpg" ,
"https://cdn.yemek.com/mncrop/940/625/uploads/2021/04/patlicanli-pilav-yemekcom.jpg"
];

Related

Trying to build Listview but I can't add any method to the Expansion tile

I have the following code to build a listview from local JSON file and it works perfectly fine. However, when I try to add a method such as onTap: (){} to the ExpansionTile in the _buildList Widget I got the following error
Error: No named parameter with the name 'onTap'. onTap: (){}, ^^^^^ /C:/src/flutter/packages/flutter/lib/src/material/expansion_tile.dart:51:9: Context: Found this candidate, but the arguments don't match. const ExpansionTile({ ^^^^^^^^^^^^^
The code in Main.dart is
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'datamodel.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<List<Menu>> ReadJsonData() async {
//read json file
final jsondata = await rootBundle.loadString('assets/data0.json');
//decode json data as list
final list = json.decode(jsondata) as List<dynamic>;
//map json and initialize using Model
return list.map((e) => Menu.fromJson(e)).toList();
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home:Scaffold(
appBar: AppBar(
title: const Text('My Title'),
),
body: FutureBuilder(
future: ReadJsonData(),
builder: (context,data){
if(data.hasError){
return Center(child: Text("${data.error}"));
}else if(data.hasData){
var items =data.data as List<Menu>;
return ListView.builder(
itemCount: items.length,
itemBuilder: (BuildContext context, int index) =>
_buildList(items[index]),
);
}else{
return Center(child: CircularProgressIndicator(),);
}
},
)
)
);
}
Widget _buildList(Menu list) {
return ExpansionTile(
leading: Icon(list.icon),
// line causing error
onTap: (){},
title: Text(
list.name!,// I added !
style: TextStyle(fontSize: list.font?.toDouble(), fontWeight: FontWeight.bold),
),
children: list.subMenu!.map(_buildList).toList(),// I added !
);
}
}
So is there any way to add the Method for each Expansion tile?
Thank you in advance!
ExpansionTile does not have an onTap property. This is because it has a default behaviour on tapping, expands or collapses.
If you'd like to execute some specific logic on expanded or collapsed, you can use onExpansionChanged:
return ExpansionTile(
onExpansionChanged: (bool expanded) {
// do what you want
},
);

How can I fix the focus on a ListView item in Flutter?

I have a listview that I want to enable shortcuts like Ctrl+c, Enter, etc this improves user experience.
The issue is after I click/tap on an item, it loses focus and the shortcut keys no longer work.
Is there a fix or a workaround for this?
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
void main() {
runApp(const MyApp());
}
class SomeIntent extends Intent {}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.orange,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetBuilder<Controller>(
init: Get.put(Controller()),
builder: (controller) {
final List<MyItemModel> myItemModelList = controller.myItemModelList;
return Scaffold(
appBar: AppBar(
title: RawKeyboardListener(
focusNode: FocusNode(),
onKey: (event) {
if (event.logicalKey.keyLabel == 'Arrow Down') {
FocusScope.of(context).nextFocus();
}
},
child: const TextField(
autofocus: true,
),
),
),
body: myItemModelList.isEmpty
? const Center(child: CircularProgressIndicator())
: ListView.builder(
itemBuilder: (context, index) {
final MyItemModel item = myItemModelList[index];
return Shortcuts(
shortcuts: {
LogicalKeySet(LogicalKeyboardKey.enter): SomeIntent(),
},
child: Actions(
actions: {
SomeIntent: CallbackAction<SomeIntent>(
// this will not launch if I manually focus on the item and press enter
onInvoke: (intent) => print(
'SomeIntent action was launched for item ${item.name}'),
)
},
child: InkWell(
focusColor: Colors.blue,
onTap: () {
print('clicked item $index');
controller.toggleIsSelected(item);
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
color: myItemModelList[index].isSelected
? Colors.green
: null,
height: 50,
child: ListTile(
title: Text(myItemModelList[index].name),
subtitle: Text(myItemModelList[index].detail),
),
),
),
),
),
);
},
itemCount: myItemModelList.length,
),
);
},
);
}
}
class Controller extends GetxController {
List<MyItemModel> myItemModelList = [];
#override
void onReady() {
myItemModelList = buildMyItemModelList(100);
update();
super.onReady();
}
List<MyItemModel> buildMyItemModelList(int count) {
return Iterable<MyItemModel>.generate(
count,
(index) {
return MyItemModel('$index - check debug console after pressing Enter.',
'$index - click me & press Enter... nothing happens\nfocus by pressing TAB/Arrow Keys and press Enter.');
},
).toList();
}
toggleIsSelected(MyItemModel item) {
for (var e in myItemModelList) {
if (e == item) {
e.isSelected = !e.isSelected;
}
}
update();
}
}
class MyItemModel {
final String name;
final String detail;
bool isSelected = false;
MyItemModel(this.name, this.detail);
}
Tested with Windows 10 and flutter 3.0.1
Using Get State manager.
In Flutter, a ListView or GridView containing a number of ListTile widgets, you may notice that the selection and the focus are separate. We also have the issue of tap() which ideally sets both the selection and the focus - but by default tap does nothing to affect focus or selection.
The the official demo of ListTile selected property https://api.flutter.dev/flutter/material/ListTile/selected.html
shows how we can manually implement a selected ListTile and get tap() to change the selected ListTile. But this does nothing for us in terms of synchronising focus.
Note: As that demo shows, tracking the selected ListTile needs to
be done manualy, by having e.g. a selectedIndex variable, then setting the
selected property of a ListTile to true if the index matches the
selectedIndex.
Here are a couple of solutions to the problem of to the syncronising focus, selected and tap in a listview.
Solution 1 (deprecated, not recommended):
The main problem is accessing focus behaviour - by default we have no access
to each ListTile's FocusNode.
UPDATE: Actually it turns out that there is a way to access a focusnode, and thus allocating our own focusnodes is not necessary - see Solution 2 below. You use the Focus widget with a child: Builder(builder: (BuildContext context) then you can access the focusnode with FocusScope.of(context).focusedChild. I am leaving this first solution here for study, but recommend solution 2 instead.
But by allocating a focus node for each ListTile item in the
ListView, we then do. You see, normally a ListTile item allocates its own focus
node, but that's bad for us because we want to access each focus node from
the outside. So we allocate the focus nodes ourselves and pass them to the
ListTile items as we build them, which means a ListTile no longer has to
allocate a FocusNode itself - note: this is not a hack - supplying custom
FocusNodes is supported in the ListTile API. We now get access to the
FocusNode object for each ListTile item, and
invoke its requestFocus()
method whenever selection changes.
we also listen in the FocusNode
objects for changes in focus, and update the selection whenever focus
changes.
The benefits of custom focus node which we supply ourselves to each ListTile
are:
We can access the focus node from outside the ListTile widget.
We can use the focus node to request focus.
We can listen to changes in focus.
BONUS: We can wire shortcuts directly into the focus node without the usual Flutter shortcut complexity.
This code synchronises selection, focus and tap behaviour, as well as supporting up and down arrow changing the selection.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
// Enhancements to the official ListTile 'selection' demo
// https://api.flutter.dev/flutter/material/ListTile/selected.html to
// incorporate Andy's enhancements to sync tap, focus and selected.
// This version includes up/down arrow key support.
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title =
'Synchronising ListTile selection, focus and tap - with up/down arrow key support';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatefulWidget(),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
late List _focusNodes; // our custom focus nodes
void changeSelected(int index) {
setState(() {
_selectedIndex = index;
});
}
void changeFocus(int index) {
_focusNodes[index].requestFocus(); // this works!
}
// initstate
#override
void initState() {
super.initState();
_focusNodes = List.generate(
10,
(index) => FocusNode(onKeyEvent: (node, event) {
print(
'focusnode detected: ${event.logicalKey.keyLabel} ${event.runtimeType} $index ');
// The focus change that happens when the user presses TAB,
// SHIFT+TAB, UP and DOWN arrow keys happens on KeyDownEvent (not
// on the KeyUpEvent), so we ignore the KeyDownEvent and let
// Flutter do the focus change. That way we don't need to worry
// about programming manual focus change ourselves, say, via
// methods on the focus nodes, which would be an unecessary
// duplication.
//
// Once the focus change has happened naturally, all we need to do
// is to change our selected state variable (which we are manually
// managing) to the new item position (where the focus is now) -
// we can do this in the KeyUpEvent. The index of the KeyUpEvent
// event will be item we just moved focus to (the KeyDownEvent
// supplies the old item index and luckily the corresponding
// KeyUpEvent supplies the new item index - where the focus has
// just moved to), so we simply set the selected state value to
// that index.
if (event.runtimeType == KeyUpEvent &&
(event.logicalKey == LogicalKeyboardKey.arrowUp ||
event.logicalKey == LogicalKeyboardKey.arrowDown ||
event.logicalKey == LogicalKeyboardKey.tab)) {
changeSelected(index);
}
return KeyEventResult.ignored;
}));
}
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return ListTile(
focusNode: _focusNodes[
index], // allocate our custom focus node for each item
title: Text('Item $index'),
selected: index == _selectedIndex,
onTap: () {
changeSelected(index);
changeFocus(index);
},
);
},
);
}
}
Important Note: The above solution doesn't work when changing the number of items, because all the focusnodes are allocated during initState which only gets called once. For example if the number of items increases then there are not enough focusnodes to go around and the build step will crash.
The next solution (below) does not explicitly allocate focusnodes and is a more robust solution which supports rebuilding and adding and removing items dynamically.
Solution 2 (allows rebuilds, recommended)
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:developer' as developer;
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter selectable listview - solution 2';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: HomeWidget(),
);
}
}
// ╦ ╦┌─┐┌┬┐┌─┐╦ ╦┬┌┬┐┌─┐┌─┐┌┬┐
// ╠═╣│ ││││├┤ ║║║│ │││ ┬├┤ │
// ╩ ╩└─┘┴ ┴└─┘╚╩╝┴─┴┘└─┘└─┘ ┴
class HomeWidget extends StatefulWidget {
const HomeWidget({super.key});
#override
State<HomeWidget> createState() => _HomeWidgetState();
}
class _HomeWidgetState extends State<HomeWidget> {
// generate a list of 10 string items
List<String> _items = List<String>.generate(10, (int index) => 'Item $index');
String currentItem = '';
int currentIndex = 0;
int redrawTrigger = 0;
// clear items method inside setstate
void _clearItems() {
setState(() {
currentItem = '';
_items.clear();
});
}
// add items method inside setstate
void _rebuildItems() {
setState(() {
currentItem = '';
_items.clear();
_items.addAll(List<String>.generate(5, (int index) => 'Item $index'));
});
}
// set currentItem method inside setstate
void _setCurrentItem(String item) {
setState(() {
currentItem = item;
currentIndex = _items.indexOf(item);
});
}
// set currentindex method inside setstate
void _setCurrentIndex(int index) {
setState(() {
currentIndex = index;
if (index < 0 || index >= _items.length) {
currentItem = '';
} else {
currentItem = _items[index];
}
});
}
// delete current index method inside setstate
void _deleteCurrentIndex() {
// ensure that the index is valid
if (currentIndex >= 0 && currentIndex < _items.length) {
setState(() {
String removedValue = _items.removeAt(currentIndex);
if (removedValue.isNotEmpty) {
print('Item index $currentIndex deleted, which was $removedValue');
// calculate new focused index, if have deleted the last item
int newFocusedIndex = currentIndex;
if (newFocusedIndex >= _items.length) {
newFocusedIndex = _items.length - 1;
}
_setCurrentIndex(newFocusedIndex);
print('setting new newFocusedIndex to $newFocusedIndex');
} else {
print('Failed to remove $currentIndex');
}
});
} else {
print('Index $currentIndex is out of range');
}
}
#override
Widget build(BuildContext context) {
// print the current time
print('HomeView build at ${DateTime.now()} $_items');
return Scaffold(
body: Column(
children: [
// display currentItem
Text(currentItem),
Text(currentIndex.toString()),
ElevatedButton(
child: Text("Force Draw"),
onPressed: () => setState(() {
redrawTrigger = redrawTrigger + 1;
}),
),
ElevatedButton(
onPressed: () {
_setCurrentItem('Item 0');
redrawTrigger = redrawTrigger + 1;
},
child: const Text('Set to Item 0'),
),
ElevatedButton(
onPressed: () {
_setCurrentIndex(1);
redrawTrigger = redrawTrigger + 1;
},
child: const Text('Set to index 1'),
),
// button to clear items
ElevatedButton(
onPressed: _clearItems,
child: const Text('Clear Items'),
),
// button to add items
ElevatedButton(
onPressed: _rebuildItems,
child: const Text('Rebuild Items'),
),
// button to delete current item
ElevatedButton(
onPressed: _deleteCurrentIndex,
child: const Text('Delete Current Item'),
),
Expanded(
key: ValueKey('${_items.length} $redrawTrigger'),
child: ListView.builder(
itemBuilder: (BuildContext context, int index) {
// print(' building listview index $index');
return FocusableText(
_items[index],
autofocus: index == currentIndex,
updateCurrentItemParentCallback: _setCurrentItem,
deleteCurrentItemParentCallback: _deleteCurrentIndex,
);
},
itemCount: _items.length,
),
),
],
),
);
}
}
// ╔═╗┌─┐┌─┐┬ ┬┌─┐┌─┐┌┐ ┬ ┌─┐╔╦╗┌─┐─┐ ┬┌┬┐
// ╠╣ │ ││ │ │└─┐├─┤├┴┐│ ├┤ ║ ├┤ ┌┴┬┘ │
// ╚ └─┘└─┘└─┘└─┘┴ ┴└─┘┴─┘└─┘ ╩ └─┘┴ └─ ┴
class FocusableText extends StatelessWidget {
const FocusableText(
this.data, {
super.key,
required this.autofocus,
required this.updateCurrentItemParentCallback,
required this.deleteCurrentItemParentCallback,
});
/// The string to display as the text for this widget.
final String data;
/// Whether or not to focus this widget initially if nothing else is focused.
final bool autofocus;
final updateCurrentItemParentCallback;
final deleteCurrentItemParentCallback;
#override
Widget build(BuildContext context) {
return CallbackShortcuts(
bindings: {
const SingleActivator(LogicalKeyboardKey.keyX): () {
print('X pressed - attempting to delete $data');
deleteCurrentItemParentCallback();
},
},
child: Focus(
autofocus: autofocus,
onFocusChange: (value) {
print(
'$data onFocusChange ${FocusScope.of(context).focusedChild}: $value');
if (value) {
updateCurrentItemParentCallback(data);
}
},
child: Builder(builder: (BuildContext context) {
// The contents of this Builder are being made focusable. It is inside
// of a Builder because the builder provides the correct context
// variable for Focus.of() to be able to find the Focus widget that is
// the Builder's parent. Without the builder, the context variable used
// would be the one given the FocusableText build function, and that
// would start looking for a Focus widget ancestor of the FocusableText
// instead of finding the one inside of its build function.
developer.log('build $data', name: '${Focus.of(context)}');
return GestureDetector(
onTap: () {
Focus.of(context).requestFocus();
// don't call updateParentCallback('data') here, it will be called by onFocusChange
},
child: ListTile(
leading: Icon(Icons.map),
selectedColor: Colors.red,
selected: Focus.of(context).hasPrimaryFocus,
title: Text(data),
),
);
}),
),
);
}
}
Edit:
this works to regain focus, however, the focus starts again from the top widget and not from the widget that was clicked on. I hope this answer still helps
Edit 2 I found a solution, you'll have to create a separate FocusNode() for each element on your listview() and requestFocus() on that in your inkwell. Complete updated working example (use this one, not the one in the original answer):
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class SomeIntent extends Intent {}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.orange,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final myItemModelList = List.generate(10, (index) => Text('${index + 1}'));
final _focusNodes = List.generate(myItemModelList.length, (index) => FocusNode());
return Scaffold(
appBar: AppBar(),
body: myItemModelList.isEmpty
? const Center(child: CircularProgressIndicator())
: ListView.builder(
itemBuilder: (context, index) {
final item = myItemModelList[index];
return RawKeyboardListener(
focusNode: _focusNodes[index],
onKey: (event) {
if (event.logicalKey.keyLabel == 'Arrow Down') {
FocusScope.of(context).nextFocus();
}
},
child: Actions(
actions: {
SomeIntent: CallbackAction<SomeIntent>(
// this will not launch if I manually focus on the item and press enter
onInvoke: (intent) => print(
'SomeIntent action was launched for item ${item}'),
)
},
child: InkWell(
focusColor: Colors.blue,
onTap: () {
_focusNodes[index].requestFocus();
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
color: Colors.blue,
height: 50,
child: ListTile(
title: myItemModelList[index],
subtitle: myItemModelList[index]),
),
),
),
),
);
},
itemCount: myItemModelList.length,
),
);
}
}
Edit 3:
To also detect the up key you can try:
onKey: (event) {
if (event.isKeyPressed(LogicalKeyboardKey.arrowDown)) {
FocusScope.of(context).nextFocus();
} else if (event.isKeyPressed(LogicalKeyboardKey.arrowUp)) {
FocusScope.of(context).previousFocus();
}
},
Original answer (you should still read to understand the complete answer).
First of all, your adding RawKeyboardListener() within your appBar() don't do that, instead add it to the Scaffold().
Now, create a FocusNode() outside of your Build method:
class MyHomePage extends StatelessWidget {
MyHomePage({Key? key}) : super(key: key);
final _focusNode = FocusNode();
#override
Widget build(BuildContext context) {}
...
...
And assing the _focusNode to the RawKeyboardListener():
RawKeyboardListener(focusNode: _focusNode,
...
And here's the key point. Since you don't want to lose focus in the ListView(), in the onTap of your inkWell you'll have to request focus again:
InkWell(
focusColor: Colors.blue,
onTap: () {
_focusNode.requestFocus();
print('clicked item $index');
},
...
That's it.
Here is a complete working example based on your code. (I needed to modify some things, since I don't have all your data):
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(const MyApp());
}
class SomeIntent extends Intent {}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.orange,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key? key}) : super(key: key);
final _focusNode = FocusNode();
#override
Widget build(BuildContext context) {
final myItemModelList = List.generate(10, (index) => Text('${index + 1}'));
return Scaffold(
appBar: AppBar(),
body: myItemModelList.isEmpty
? const Center(child: CircularProgressIndicator())
: RawKeyboardListener(
focusNode: _focusNode,
onKey: (event) {
if (event.logicalKey.keyLabel == 'Arrow Down') {
FocusScope.of(context).nextFocus();
}
},
child: ListView.builder(
itemBuilder: (context, index) {
final item = myItemModelList[index];
return Shortcuts(
shortcuts: {
LogicalKeySet(LogicalKeyboardKey.enter): SomeIntent(),
},
child: Actions(
actions: {
SomeIntent: CallbackAction<SomeIntent>(
// this will not launch if I manually focus on the item and press enter
onInvoke: (intent) => print(
'SomeIntent action was launched for item ${item}'),
)
},
child: InkWell(
focusColor: Colors.blue,
onTap: () {
_focusNode.requestFocus();
print('clicked item $index');
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
color: Colors.blue,
height: 50,
child: ListTile(
title: myItemModelList[index],
subtitle: myItemModelList[index]),
),
),
),
),
);
},
itemCount: myItemModelList.length,
),
),
);
}
}
Demo:

Flutter Dropdown Button and Dropdown Handler different size

I want to create a dropdown menu on flutter where the handler button that opens the dropdown uses just an icon and the menu list opened by it uses an icon and a text.
I almost manage to create it, as you can check on the following screenshots:
Closed
Opened
I'm struggling with the opened width, so my question is how to give the opened menu enough width and keep the handler button on its current width.
Notice that I want the dropdown to be at the end of the Row, so consider this black box to be an area of something else, nothing important.
I'm adding the relevant code below and the complete code on the following links.
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Question Dropdown",
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(
optionStream: BehaviorSubject<Option>(),
),
);
}
}
class HomePage extends StatelessWidget {
final BehaviorSubject<Option> optionStream;
const HomePage({
Key? key,
required this.optionStream,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Question Dropdown"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
children: [
Expanded(
child: Container(
height: 48,
color: Colors.black,
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: StreamBuilder<Option>(
initialData: Option.A,
stream: optionStream,
builder: (context, snapshot) {
final option = snapshot.data ?? Option.A;
return _dropDownMenu(context, option);
},
),
),
],
),
],
),
),
);
}
Widget _dropDownMenu(
BuildContext context,
Option option,
) {
const items = Option.values;
return DropdownButtonHideUnderline(
child: DropdownButton<Option>(
value: option,
selectedItemBuilder: (context) =>
items.map((e) => _dropdownHandler(context, e)).toList(),
items: items.map((e) => _dropdownItem(context, e)).toList(),
onChanged: (e) => optionStream.add(e ?? Option.A),
),
);
}
OptionsItemHelper _dropDownItemData(
BuildContext context,
Option option,
) {
Widget icon;
String text;
switch (option) {
case Option.A:
icon = const Icon(Icons.ac_unit);
text = "An option";
break;
case Option.B:
icon = const Icon(Icons.baby_changing_station);
text = "Best option";
break;
case Option.C:
icon = const Icon(Icons.cake_sharp);
text = "Closest option";
break;
case Option.D:
icon = const Icon(Icons.dashboard);
text = "Dumb option";
break;
}
return OptionsItemHelper(text, icon);
}
Widget _dropdownHandler(
BuildContext context,
Option option,
) {
final helper = _dropDownItemData(context, option);
return helper.icon;
}
DropdownMenuItem<Option> _dropdownItem(
BuildContext context,
Option option,
) {
final helper = _dropDownItemData(context, option);
return DropdownMenuItem<Option>(
value: option,
child: Row(
children: [
helper.icon,
const SizedBox(width: 16),
Text(helper.text),
],
),
);
}
}
enum Option {
A,
B,
C,
D,
}
class OptionsItemHelper {
final String text;
final Widget icon;
OptionsItemHelper(
this.text,
this.icon,
);
}
Complete code on Github
Complete code on Gitlab
I did find a workaround using GestureDetector and showMenu, I'm sharing here and pushing to the repo as "workaround" commit in case you need the same as I need now, I'm keeping the question without answer in case someone finds a better way using the dropdown.
The new dropDownMenu function
Widget _dropDownMenu(
BuildContext context,
Option option,
) {
const items = Option.values;
return GestureDetector(
onTapDown: (details) async {
final offset = details.globalPosition;
final newOption = await showMenu(
context: context,
position: RelativeRect.fromLTRB(offset.dx, offset.dy, 0, 0),
items: items.map((e) => _dropdownItem(context, e, option)).toList(),
);
if (newOption != null) {
optionStream.add(newOption);
}
},
child: _dropdownHandler(context, option),
);
}
and the new dropdownItem function.
PopupMenuEntry<Option> _dropdownItem(
BuildContext context,
Option option,
Option selected,
) {
final helper = _dropDownItemData(context, option);
return CheckedPopupMenuItem<Option>(
value: option,
checked: option == selected,
child: Row(
children: [
Expanded(child: Container()),
Text(helper.text),
const SizedBox(width: 16),
helper.icon,
],
),
);
}
How it looks like
Closed
Opened
Bigger Screen

How to input a dropdown or is it possible to input a dropdown within a listview builder using maps from an other class file?

I am creating an order application for a cafe and on this I have a list of the cafes items outside of the main class as its own file that I created using maps and in the main dart I have already called to the map class outside of the file and put it into a listview builder however I want there to be dropdowns too within the main page. I want the listviewer and the dropdownbuttons to be connected but I don't know if I can use the maps within the dropdown or do I have to turn them into lists and what not. Also if we can used maps within the dropdownbuttons how do we define it as an "items" if the dropdownbutton.
here is my map file
class Menu {
Menu();
Menu.s(this._sandwichMap);
Menu.b(this._burgerMap);
Menu.o(this._otherItemsMap);
Menu.q(this._sidesMap);
Menu.p(this._pizzaMap);
Map<String, double> _sandwichMap = {
"Veggie Melt": 4.50,
"Crispy Chicken Wrap": 6.95,
"Italian Meatball Sub": 6.99,
"Chicken Parm Grinder": 6.59,
"Grill Cheese": 3.59,
"Grilled Ham & Cheese": 4.59,
"Bacon Bagel Melt": 5.29};
Map<String, double> getSandwichMap() {
return this._sandwichMap;
}
//check burger prices again
Map<String, double> _burgerMap = {
"Veggie Burger": 4.99,
"The Quantum Burger": 7.25,
"Cafe Melt": 6.59,
"The Bull Rider": 5.79,
"Double Cheese Burger": 5.89,
"Hamburger": 3.99};
Map<String, double> getBurgerMap() {
return this._burgerMap;
}
Map<String, double> _otherItemsMap = {
"Chicken Quesadilla": 6.79,
"Cheese Quesadilla": 6.29,
"Chicken Strips": 4.99,
"Popcorn Chicken": 4.59,
"Jalapeno Poppers": 3.49};
Map<String, double> getOtherItemsMap() {
return this._otherItemsMap;
}
Map<String, double> _sidesMap = {
"French Fries": 3.29,
"Onion Rings": 4.79,
"Jalapeno Cheese Curds": 4.99,
"Tater Tots": 3.19,
"Pretzel Bites": 4.59,
"Nachos & Cheese": 3.50};
Map<String, double> getSidesMap() {
return this._sidesMap;
}
Map<String, double> _pizzaMap = {
"7-inch Cheese": 4.59,
"7-inc with topping": 4.99};
Map<String, double> getPizzaMap() {
return this._pizzaMap;
}
}
And here is my main file with the dropdowns
import 'dart:core';
import 'package:flutter/material.dart';
//import 'package:flutter/foundation.dart';
import 'package:flutter/cupertino.dart';
import 'maps.dart';
//var menu = Menu();
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
//Always use Stateless first then use stateful or stateless widgets afterward
#override
Widget build(BuildContext context) {
return MaterialApp(
//only used at the beginning of the program
title: 'The Cafe',
//just a title to the app it does not show for there is nothing telling it to show on the screen
debugShowCheckedModeBanner: false,
//takes out the ribbon at the top right corner of the screen and app
theme: ThemeData(
primarySwatch: Colors.green,
brightness: Brightness.dark,
fontFamily: 'georgia',
textTheme: TextTheme(headline1: TextStyle(fontSize: 100))
//controls the color of the very top part of the application
),
home: StartPage(),
//used to connect the Stateless widget to the Stateful widget below
);
}
}
class StartPage extends StatefulWidget {
#override
_StartPageState createState() => _StartPageState();
}
// do not forget the } prior to this comment if you do it will result in error and the program does not known why either
class _StartPageState extends State<StartPage> {
var menu = Menu();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('The Campus Cafe'),
//where the main title is computed to be shown on the screen
centerTitle: true,
//centers the title
),
body: Center(
//This is Header that is after the main Title
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
//Header Container
Expanded(
child: Image.asset(
'assets/images/campus-cafe-logo-350sidebar.png',)
),
Container(
padding: const EdgeInsets.all(8.0),
alignment: Alignment.center,
child: Text("Our Menu", style: TextStyle(fontSize: 30),
),
),
Expanded(
child: ListView.builder(
itemCount: 5,
itemBuilder: (context, int index) {
String key = menu.getSandwichMap().keys.elementAt(index);
List<String> key1 = menu.getSandwichMap().keys.toList();
//List<double> key2 = menu.getSandwichMap().values.toList();
return DropdownButton(
value: key[index],
items: key,
onChanged: (value) {
key = value;
setState(() {});
},
hint: Text('Sandwich'),
);
return DropdownButton(items: [],
);
}),
),
]
),
),
);
}
}
If more information is needed on how I want my dropdowns too look I want them too look like the file below with just the listview.builder but I want that information to be within a dropdown if possible
import 'dart:core';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/cupertino.dart';
import 'maps.dart';
//var menu = Menu();
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
//Always use Stateless first then use stateful or stateless widgets afterward
#override
Widget build(BuildContext context) {
return MaterialApp(
//only used at the beginning of the program
title: 'The Cafe',
//just a title to the app it does not show for there is nothing telling it to show on the screen
debugShowCheckedModeBanner: false,
//takes out the ribbon at the top right corner of the screen and app
theme: ThemeData(
primarySwatch: Colors.green,
brightness: Brightness.dark,
fontFamily: 'georgia',
textTheme: TextTheme(headline1: TextStyle(fontSize: 100))
//controls the color of the very top part of the application
),
home: StartPage(),
//used to connect the Stateless widget to the Stateful widget below
);
}
}
class StartPage extends StatefulWidget {
#override
_StartPageState createState() => _StartPageState();
}
// do not forget the } prior to this comment if you do it will result in error and the program does not known why either
class _StartPageState extends State<StartPage> {
var menu = Menu();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('The Campus Cafe'),
//where the main title is computed to be shown on the screen
centerTitle: true,
//centers the title
),
body: Center(
//This is Header that is after the main Title
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
//Header Container
Expanded(
child: Image.asset(
'assets/images/campus-cafe-logo-350sidebar.png',)
),
Container(
padding: const EdgeInsets.all(8.0),
alignment: Alignment.center,
child: Text("Our Menu", style: TextStyle(fontSize: 30),
),
),
Expanded(
child: ListView.builder(
itemCount: menu.getSandwichMap().length,
itemBuilder: (context, int index) {
String key = menu.getSandwichMap().keys.elementAt(index);
return new Column(
children: <Widget>[
//DropdownButton<String>(
//isExpanded: true;
//items: menu.getSandwichMap();
new ListTile(
title: new Text("$key"),
subtitle: new Text("${menu.getSandwichMap()[key]}"),
),
new Divider(
height: 2.0,
),
// ),
],
);
},
),
)
]
),
),
);
}
}
It is not that difficult to build a dropdown field from your map values.
Basically the idea is you need to get a List<String> first. Then, your life will be much easier. How you want to get there, is up to you.
I have created a DartPad example using your values. Try have a look there.
https://dartpad.dev/93b680c13c57e7a8faff99116793cd3f
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
final List<String> sandwichItems = sandwichMap.keys.toList();
String dropdownValue;
Widget build(BuildContext context) {
return
Container(
child: DropdownButton<String>(
value: dropdownValue ?? sandwichItems[0],
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
items: sandwichItems.map((e){
return DropdownMenuItem<String>(
value: e,
child: Text(e),
);
}).toList(),
),
);
}
}
Map<String, double> sandwichMap = {
"Veggie Melt": 4.50,
"Crispy Chicken Wrap": 6.95,
"Italian Meatball Sub": 6.99,
"Chicken Parm Grinder": 6.59,
"Grill Cheese": 3.59,
"Grilled Ham & Cheese": 4.59,
"Bacon Bagel Melt": 5.29};

flutter firestore push document data to details screen

I need to click on the list, and open a new detail page. I've looked everywhere for examples of how to do this and I found many. However my data is structured different and even though my code has no errors it will not populate the details page. Here is what i'm working with:
//page 1
child: ListView.builder(
itemCount:
_documents == null ? 0 : _documents.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
"assets/images/roomfield1.png"),
fit: BoxFit.fitWidth,
alignment: Alignment.topCenter),
color:
Theme.of(context).canvasColor,
),
child: ListTile(
title: Text(_documents[index]
['title']),
subtitle: Text(_documents[index]
["description"]),
trailing: Text(_documents[index]
['citystate']),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
DetailPage(_documents[index])));
})));
},
Here is the details page:
// details page
import 'package:flutter/material.dart';
class DetailPage extends StatefulWidget {
DetailPage(_documents, {Key key,}) : super(key: key);
#override
_DetailPageState createState() => _DetailPageState();
}
class _DetailPageState extends State<DetailPage> {
var _documents;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text('Details Page'),
),
body: ListView.builder(
itemCount: _documents == null ? 0 : _documents.length,
itemBuilder: (BuildContext context,[index]) {
return ListTile(
title: Text(_documents[index]['title']),
);
}));}}
My documents use the documentId. i tried passing the id and then using it in a stream builder and that didn't work either. I think my code is close, but I don't know what is wrong. Also I cant change the structure of the data, because it is used all over the place in this format. I'm guessing its something simple that i missed, maybe the constructor on the details page but i'm not sure...Any insight is appreciated.
You created a variable called _documents at the top of the class. However, to retrieve the value of the actual _documents variable in the DetailPage class, you need to use widget.documents. I have provided the correct code below for you to use.
Also, I don't know why you're using a StatefulWidget for your DetailPage class since you don't seem to be doing anything that requires state management. You could easily make this a StatelessWidget but maybe there's more code that I'm unable to see.
// details page
import 'package:flutter/material.dart';
class DetailPage extends StatefulWidget {
final var documents; // created new final variable that will be initialized by the constructor
DetailPage(this.documents);
#override
_DetailPageState createState() => _DetailPageState();
}
class _DetailPageState extends State<DetailPage> {
//var _documents; // Don't use this
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text('Details Page'),
),
body: ListView.builder(
itemCount: widget.documents == null ? 0 : widget.documents.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(widget.documents[index]['title']), // don't know why you need to specify the index since you already did in your other classs
);
}));}}