How to implement cycle wheel scroll list widget - flutter

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),
),
);
}),
);
}
}

Related

How to Automaticallly scroll to a position of a Row inside SingleChildScrollView in Flutter

I am using a SingleChildScrollView. Its scrollDirection is set to Horizontal with >20 child widgets placed inside a Row Widget. I want to programmatically scroll to a position widget(i.e, 5th or 6th position) in the Row. Is there any way to do it programmatically ?
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: buttons,
),
)
The easiest way to doing this is using Scrollable.ensureVisible.
ensureVisible method
Scrolls the scrollables that enclose the given
context so as to make the given context visible.
Please see the code below :
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _value = 0;
static final List<GlobalKey> _key = List.generate(20, (index) => GlobalKey());
final List<Widget> buttons = List.generate(
20,
(index) => RaisedButton(
onPressed: () {},
color: index % 2 == 0 ? Colors.grey : Colors.white,
child: Text("Button No # ${index + 1}", key: _key[index]),
),
);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: buttons,
),
),
DropdownButton(
value: _value,
items: List.generate(
20,
(index) => DropdownMenuItem(
child: Text("Goto Button # ${index + 1}"), value: index),
),
onChanged: (value) {
setState(() {
_value = value;
print("calling");
Scrollable.ensureVisible(_key[value].currentContext);
});
},
)
],
),
);
}
}
You could define a ScrollController:
ScrollController _controller = new ScrollController();
Pass it to the SingleChildScrollView:
SingleChildScrollView(
controller: _scrollController,
scrollDirection: Axis.horizontal,
child: Row(
children: buttons,
),
),
And programmatically scroll it as follows:
void scroll(double position) {
_scrollController.jumpTo(position);
}
Or, if a scroll animation is desired:
void scrollAnimated(double position) {
_scrollController.animateTo(position, Duration(seconds: 1), Curves.ease);
}
If you'd like to automatically scroll immediately after the layout has been built, you could do so by overriding the initState method:
#override
void initState() {
super.initState();
WidgetsBinding.instance
.addPostFrameCallback((_) => scroll(500)); // scroll automatically 500px (as an example)
}
You can add ScrollController in SingleChildScrollView and scroll to your specific position which you want
_scrollController.animateTo(
//here specifing position= 100 mean 100px
100,
curve: Curves.ease,
duration: Duration(seconds: 1),

Flutter touch events on inidividual letters of a string

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

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

CustomScrollView within ExpansionTile error

Tried to fix this reading some documentation and some open issues but was not lucky.. Could someone please help?
I am getting this error:
type 'bool' is not a subtype of type 'double' in type cast
Not sure why though I tried adding container wrapping the component, adding height, adding flexible box etc...
No lucky
`import 'package:flutter/material.dart';
class SampleData {
SampleData(this.title, [this.children = const <SizedBox>[]]);
final String title;
final List<SizedBox> children;
}
final List<SampleData> data = <SampleData>[
SampleData("IT", [
SizedBox(
height: 300,
width: 300,
child: CustomScrollView(
scrollDirection: Axis.horizontal,
slivers: <Widget>[
new SliverToBoxAdapter(
child: Text('fesfefes'),
),
],
),
),
]),
];
class Branch extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Test 123'),
),
body: Container(
width: 500,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) => Item(data[index]),
itemCount: data.length,
),
),
);
}
}
// Displays one Entry. If the entry has children then it's displayed
// with an ExpansionTile.
class Item extends StatelessWidget {
const Item(this.sample);
final SampleData sample;
Widget _buildTiles(SampleData root) {
return SizedBox(
width: 500,
child: ExpansionTile(
key: PageStorageKey<SampleData>(root),
title: Text(root.title),
children: root.children,
),
);
}
#override
Widget build(BuildContext context) {
return _buildTiles(sample);
}
}
`
You can copy paste run full code blow
You can remove
//key: PageStorageKey<SampleData>(root),
working demo
full code
import 'package:flutter/material.dart';
class SampleData {
SampleData(this.title, [this.children = const <SizedBox>[]]);
final String title;
final List<SizedBox> children;
}
final List<SampleData> data = <SampleData>[
SampleData("IT", [
SizedBox(
height: 300,
width: 300,
child: CustomScrollView(
scrollDirection: Axis.horizontal,
slivers: <Widget>[
new SliverToBoxAdapter(
child: Text('fesfefes'),
),
],
),
),
]),
];
class Branch extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Test 123'),
),
body: Container(
width: 500,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) => Item(data[index]),
itemCount: data.length,
),
),
);
}
}
// Displays one Entry. If the entry has children then it's displayed
// with an ExpansionTile.
class Item extends StatelessWidget {
const Item(this.sample);
final SampleData sample;
Widget _buildTiles(SampleData root) {
return SizedBox(
width: 500,
child: ExpansionTile(
//key: PageStorageKey<SampleData>(root),
title: Text(root.title),
children: root.children,
),
);
}
#override
Widget build(BuildContext context) {
return _buildTiles(sample);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Branch(),
);
}
}

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: