Flutter touch events on inidividual letters of a string - flutter

I want to be able to detect touch events on individual letters of a string.
For example, if the string is "HELLO", I need to distinguish between tapping on "H" or the "O". The actual strings will be longer than that, around 500 characters.
Naively, I thought about wrapping all characters them into individual Text widgets. Is there a better approach?

Check out this example where I have create a sample example via which you will get an idea for you implementation.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SampleApp(),
debugShowCheckedModeBanner: false,
);
}
}
class SampleApp extends StatefulWidget {
#override
_SampleAppState createState() => _SampleAppState();
}
class _SampleAppState extends State<SampleApp> {
List<String> sampleStrings = ['H', 'E', 'L', 'L', "O"];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Your heading'),
),
body: Container(
height: 80,
padding: EdgeInsets.all(15),
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: sampleStrings.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
print('This is the tapped letter : ${sampleStrings[index]}');
},
child: Card(
color: Colors.white,
margin: EdgeInsets.all(8),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
'${sampleStrings[index]}',
style: TextStyle(color: Colors.black),
),
)),
);
},
),
),
);
}
}
Let me know if it works

You can try it with a Gesture Detector. but even with that you may need to use individual letters as separate Gesture Detectors.
https://api.flutter.dev/flutter/widgets/GestureDetector-class.html

Related

StreamBuilder stuck in connectionState.waiting in Flutter

I am trying to create a todo list application, i used Streambuilder to show list of Streams.
it is a simple application there is a button to add new task it is a floatingActionButton and a StreamController to manage the data, and have a TabBar with two tabs first and second so the StreamBuilder is in the first tab and other tab just contain a string in the center for now.
I can add tasks to the StreamController perfectly, but there is two issues:
1- when the program runs StreamBuilder stuck in ConnectionSatate.waiting if the Stream is null.
2- when i click second tab and came back to first tab the it also stuck in ConnectionSatate.waiting even my stream has data in it, and when i click add button to add new data it shows the data and the new one again.
here is my whole code:
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final StreamController<List<String>> streamController =
StreamController<List<String>>.broadcast();
List<String> list = [];
#override
void initState() {
super.initState();
}
#override
void dispose() {
streamController.close();
super.dispose();
}
int i = 0;
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
bottom: PreferredSize(
preferredSize: Size.fromHeight(0),
child: Container(
height: 30,
width: MediaQuery.of(context).size.width,
child: TabBar(
labelColor: Theme.of(context).iconTheme.color,
indicatorColor: Colors.green.shade600,
tabs: [
Tab(text: 'first'),
Tab(text: 'second'),
]),
),
),
),
body: TabBarView(children: [
tasks(),
Center(
child: Text('SECOND TAB'),
)
]),
floatingActionButton: TextButton(
onPressed: () {
list.add('data ${++i}');
streamController.sink.add(list);
},
child: Icon(
Icons.add,
)),
),
);
}
Padding tasks() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 5),
child: Column(
children: [
Expanded(
child: StreamBuilder(
stream: streamController.stream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (!snapshot.hasData) {
return const Center(
child: Text('There is no data'),
);
}
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
snapshot.data![index],
style: const TextStyle(
fontSize: 25,
),
),
));
})),
],
),
);
}
}
here is a video of my problem
I tried without StreamController but when i came back to first tab it shows the error that i can't listen to a Stream multiple times.
here is the answer, you need to use AutomaticKeepAliveClientMixin
I wrote a demo code sample for you https://dartpad.dartlang.org/?id=52e8e2ef8bad97f21dc91fa420dcec0e

How to make short ListView of DropDownButtons build faster?

I have a short ListView of a maximum of 10 items. Each list item will contain a DropDownButton which will hold around 1K DropDownMenuItems for selections.
In native Android, I was able to implement one that performed very smoothly, but with Flutter it takes a while to build the ListView which causes the UI to freeze.
In my case, I will need to rebuild the ListView upon every change in one of its items, so It will be a major issue.
Is there a way to make the ListView build faster, or at least be able to display a ProgressBar till it builds?
N.B: Using --profile configuration to simulate a release version improves the performance a lot, but still there is a sensed freeze.
Here's my sample code which you can directly copy/paste if you want to test it yourself.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool showList = false;
final List<DropdownMenuItem<int>> selections = List.generate(
1000,
(index) => DropdownMenuItem<int>(
value: index,
child: Text("$index"),
),
);
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Container(
width: double.infinity,
child: Column(
children: [
ElevatedButton(
child: Text("toggle list visibility"),
onPressed: () {
setState(() {
showList = !showList;
});
},
),
Expanded(
child: showList
? ListView.builder(
cacheExtent: 2000,
itemCount: 10,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Container(
height: 200,
color: Colors.green,
child: Column(
children: [
Text("List Item: $index"),
DropdownButton<int>(
onChanged: (i) {},
value: 1,
items: selections,
),
],
),
),
),
);
})
: Text("List Not Built"),
),
],
),
),
),
);
}
}
Load dropdown when clicking the button.
Add this widget on your main List View
InkWell(
onTap: () {
showDialog(
context: context,
builder: (_) {
return VendorListAlert(selectVendor: selectVendorTap);
});
},
child: // create a widget, looks like your drop down
),
Handle tap event
void selectVendorTap(pass your model){
// logic
}
Sample for custom Alert
No need to create a mutable widget, the immutable widget is better.
class VendorListAlert extends StatefulWidget {
final Function selectVendor;
const VendorListAlert({Key key, this.selectVendor}) : super(key: key);
#override
_VendorListAlertState createState() => _VendorListAlertState();
}
class _VendorListAlertState extends State<VendorListAlert> {
List<UserModel> _searchVendor = [];
#override
void initState() {
super.initState();
_searchVendor = List.from(ypModel);
}
#override
Widget build(BuildContext context) {
return AlertDialog(
content: Container(
width: width,
child: ListView.builder(
shrinkWrap: true,
itemCount: _searchVendor.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: InkWell(
onTap: () {
widget.selectVendor(_searchVendor[index]);
Navigator.pop(context);
},
child:
),
);
},
),
),
);
}
}

Is it possible to initiate a 2 dimensional array of SizedBox (as an example, I need some kind of square) in Flutter

I want to program a chess game in Flutter so first I need to make my own board. For that, I thought I can initiate a 2 dimension array of SizedBox-es (again, not necessarily) and color it just like a real chess board.
But is it possible?
You can copy paste run full code below
You can directly use package https://pub.dev/packages/flutter_chess_board or reference it's source code
code snippet of buildChessBoard
https://github.com/deven98/flutter_chess_board/blob/c8042f2aa499158c10b87aca339a9a19198ce2f3/lib/src/chess_board.dart#L182
var whiteSquareList = [
[
"a8",
"b8",
"c8",
"d8",
"e8",
"f8",
"g8",
"h8",
...
Widget buildChessBoard() {
return Column(
children: widget.whiteSideTowardsUser
? whiteSquareList.map((row) {
return ChessBoardRank(
children: row,
);
}).toList()
: whiteSquareList.reversed.map((row) {
return ChessBoardRank(
children: row.reversed.toList(),
);
}).toList(),
);
}
...
class ChessBoardRank extends StatelessWidget {
/// The list of squares in the rank
final List<String> children;
ChessBoardRank({this.children});
#override
Widget build(BuildContext context) {
return Expanded(
flex: 1,
child: Row(
children: children
.map(
(squareName) => BoardSquare(squareName: squareName),
)
.toList(),
),
);
}
}
working demo
full example code
import 'package:flutter/material.dart';
import 'package:flutter_chess_board/src/chess_board.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ChessBoard(
onMove: (move) {
print(move);
},
onCheckMate: (color) {
print(color);
},
onDraw: () {},
size: MediaQuery.of(context).size.width,
enableUserMoves: true,
)
],
),
),
);
}
}
A better option is to add a gridView like this:
GridView.builder(
itemCount: 64,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 8,
crossAxisSpacing: 4.0,
mainAxisSpacing: 4.0),
itemBuilder: (BuildContext context, int index) {
return Container(
color: index%2 == 0 ? Colors.white : Colors.black
);
},
)
If you have SizedBox instead, it will be difficult for you to add color, coin image, and alignment etc

Displaying scrollable list inside Flutter Show Dialog

I have a dynamic list of names of countries. I want to show this list in a Dialog Box (say when a user clicks a button).
Here is my implementation of the Dialog Box so far:
List<String> countries = [
'India','Japan','China','USA','France','Egypt','Norway','Nigeria','Colombia','Australia','South Korea','Bangladesh','Mozambique','Canada','Germany','Belgium','Vietnam','Bhutan','Israel','Brazil'
];
#override
Widget build(BuildContext context) {
return Dialog(
child: Container(
width: 400,
height: 400,
child: SingleChildScrollView(
child: ListView.builder(
shrinkWrap: true,
itemCount: countries.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Text('${countries[index]}'),
);
}),
),
));
}
}
The output I am getting is as follows:
Clearly, only 7 countries are visible.
I have tried several other options:
Making height of the Container as double.maxFinite.
Putting another Column inside SingleChildScrollView.
All possible permutations of Column, Container, SingleChildScrollView
However, none of the above methods seem to work (overflow error, limited number of displayed items etc).
What I actually want is to be able to show a list using ListView.builder method inside a ShowDialog.
Solved like this:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> _countries = [
'India',
'Japan',
'China',
'USA',
'France',
'Egypt',
'Norway',
'Nigeria',
'Colombia',
'Australia',
'South Korea',
'Bangladesh',
'Mozambique',
'Canada',
'Germany',
'Belgium',
'Vietnam',
'Bhutan',
'Israel',
'Brazil'
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Push for open list dialog',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _showDialogOnButtonPressing,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
_showDialogOnButtonPressing() => showDialog(
context: context,
child: Container(
width: MediaQuery.of(context).size.width - 40,
height: MediaQuery.of(context).size.height - 60,
child: Dialog(
child: ListView.builder(
itemCount: _countries.length,
itemBuilder: (context, index) => ListTile(
title: Text('${index + 1}. ${_countries[index]}'),
),
),
),
),
);
}
The result is in the image, and you can scroll up and down without problems:

How to implement cycle wheel scroll list widget

Flutter has ListWheelScrollView widget but I want cycle wheel scroll widget. Any ideas how to implement such widget.
How it should work:
For example, I have a list with 10 items and a selected item is 1
The selected element is positioned by center
above this element, you see the last element in the list below the second element
[10]
-> [1] <-
[2]
scroll down
[9]
-> [10] <-
[1]
etc.
Thanks!
You are right considering ListWheelScrollView.
The exact solution is to use ListWheelScrollView.useDelegate with ListWheelChildLoopingListDelegate.
Example:
import 'package:flutter/material.dart';
const String kTitle = 'Loop Wheel Demo';
void main() => runApp(new LoopWheelDemo());
class LoopWheelDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: kTitle,
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new HomePage(),
);
}
}
class HomePage extends StatelessWidget {
HomePage({Key key,}) : super(key: key);
#override
Widget build(BuildContext context) {
final _style = Theme.of(context).textTheme.display2;
return new Scaffold(
appBar: new AppBar(
title: new Text(kTitle),
),
body: new Center(
child: new ConstrainedBox(
constraints: BoxConstraints(
// Set height to one line, otherwise the whole vertical space is occupied.
maxHeight: _style.fontSize,
),
child: new ListWheelScrollView.useDelegate(
itemExtent: _style.fontSize,
childDelegate: ListWheelChildLoopingListDelegate(
children: List<Widget>.generate(
10, (index) => Text('${index + 1}', style: _style),
),
),
),
),
),
);
}
}
I've been trying to figure it out how to do that. I've tried lots of options, but the one that made me achieve what I wanted and what you are asking for was using the plugin:
Flutter Swiper (https://pub.dartlang.org/packages/flutter_swiper).
It's pretty customizable e flexible.
Here is the screenshot: https://imgur.com/a/ktxU6Hx
This is how I implemented it:
import 'package:flutter/material.dart';
import 'package:flutter_swiper/flutter_swiper.dart';
class Looping extends StatefulWidget {
#override
LoopingState createState() {
return new LoopingState();
}
}
class LoopingState extends State<Looping> {
List<int> numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
List<String> options = ['A', 'B', 'C', 'D'];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Infinity Loop Items'),),
body: Center(
child: Container(
height: 100.0,
child: Row(
children: <Widget>[
mySwiper(numbers),
mySwiper(options),
],
),
),
),
);
}
Widget mySwiper(List list) {
return Expanded(
child: Swiper(
itemCount: list.length,
scrollDirection: Axis.vertical,
control: SwiperControl(),
itemBuilder: (BuildContext context, int index) {
return Center(
child: Text(
list[index].toString(),
style: TextStyle(fontSize: 20.0),
),
);
}),
);
}
}