Is it possible to initiate a 2 dimensional array of SizedBox (as an example, I need some kind of square) in Flutter - 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

Related

Simple Riverpod example beyond the counter app: sum of two cells

I'm learning Flutter and I'm stuck on state management. I took a look at Riverpod and it looks promising, but I have a hard time to go beyond the counter app to something more complicated.
For example, I want to have two TextFields that collect numbers, and another Text widget to display the sum of the two TextField values. Here's what I have.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
void main() {
runApp(ProviderScope(
child: MyApp(),
));
}
class MyApp extends HookWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(title: 'Adding two cells'),
);
}
}
final cellProvider = StateProvider((_) => <int>[0, 0]);
class MyHomePage extends HookWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Consumer(builder: (context, watch, _) {
print(watch(cellProvider).state);
num _sum = watch(cellProvider).state[0] + watch(cellProvider).state[1];
return Center(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Cell(0),
Cell(1),
],
),
SizedBox(
height: 100,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Sum: ${_sum.toString()}'),
],
),
],
),
);
}),
);
}
}
class Cell extends HookWidget {
Cell(this.index);
final int index;
#override
Widget build(BuildContext context) {
return Container(
width: 150,
padding: EdgeInsets.all(30),
child: TextField(
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
onChanged: (value) {
context.read(cellProvider).state[index] = num.tryParse(value);
},
),
);
}
}
The Text widget does not update. Any suggestion?
Thank you very much,
Tony
The provider only updates when the object it provides changes, just as a Stream returns a final value you need to update the whole object (List<int>) so the consumer updates properly, changing inner values of an iterable won't trigger an update
onChanged: (value) {
final List<int> myList = context.read(cellProvider).state;
myList[index] = num.tryParse(value);
context.read(cellProvider).state = myList; //update the state with a new list
},

Flutter: Detect rebuild of any widget which is not visible on screen but is in the widget tree

Summary:
As showing a page/route using the Navigator, a new branch is created from the nearest MaterialApp parent. Meaning both pages (Main & New) will be in memory and will rebuild if they are listening to the same ChangeNotifier.
I am having trouble finding out which widget is on-screen currently visible to the user.
I need this to handle a scenario to skip performing asynchronous or long processes with some side effects, from a widget that might be in the widget tree but currently not visible.
Note: The sample code given here represents the basic architecture of the app I am currently working on, but reproduces the exact problem.
I am having this problem with a very different and complex widget tree that I have in my app, executing the doLongProcess() from a widget that is not visible on the screen. Also doLongProcess() changes some common property in my app which causes an issue, as any background widget can modify the details which are visible on the other widget.
I am looking for a solution to this issue, if there's any other way to achieve the goal except finding which widget is on the screen then please let me know that as well.
My final goal is to allow the long process to be executed from only the visible widget(s).
Please run the app once, to understand the following details properly.
Note 2:
I have tried to use mounted property of the state to determine if it can be used or not but it shows true for both widgets (MainPage TextDisplay and NewPage TextDisplay)
Let me know in the comments if more details or I missed something which is required.
Use the following sample code with provider dependency included for reproducing the problem:
// add in pubspec.yaml: provider: ^4.3.2+1
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
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: 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> {
#override
Widget build(BuildContext context) {
print('MainPage: build');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextDisplay(
name: 'MainPage TextDisplay',
),
SizedBox(
height: 20,
),
RaisedButton(
child: Text('Open New Page'),
onPressed: () => Navigator.of(context).push(MaterialPageRoute(
builder: (context) => NewPage(),
)),
),
],
),
),
);
}
}
class TextDisplay extends StatefulWidget {
final String name;
const TextDisplay({Key key, #required this.name}) : super(key: key);
#override
_TextDisplayState createState() => _TextDisplayState();
}
class _TextDisplayState extends State<TextDisplay> {
#override
Widget build(BuildContext context) {
return Container(
child: ChangeNotifierProvider.value(
value: dataHolder,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(child: Text(widget.name)),
SizedBox(
height: 20,
),
Consumer<DataHolder>(
builder: (context, holder, child) {
// need to detect if this widget is on the screen,
// only then we should go ahead with this long process
// otherwise we should skip this long process
doLongProcess(widget.name);
return Text(holder.data);
},
),
RaisedButton(
child: Text('Randomize'),
onPressed: () => randomizeData(),
),
],
),
),
);
}
void doLongProcess(String name) {
print('$name: '
'Doing a long process using the new data, isMounted: $mounted');
}
}
class NewPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
print('NewPage: build');
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: true,
title: Text('New Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextDisplay(
name: 'NewPage TextDisplay',
),
],
),
),
);
}
}
/////////////////// Data Holder Class and methods ///////////////////
class DataHolder extends ChangeNotifier {
String _data;
String get data => _data ?? 'Nothing to show, Yet!';
setData(String newData) {
print('\n new data found: $newData');
_data = newData;
notifyListeners();
}
}
final dataHolder = DataHolder();
randomizeData() {
int mills = DateTime.now().millisecondsSinceEpoch;
dataHolder.setData(mills.toString());
}
Posting solution for others to refer.
Refer to this flutter plugin/package:
https://pub.dev/packages/visibility_detector
The solution code:
// add in pubspec.yaml: provider: ^4.3.2+1
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:visibility_detector/visibility_detector.dart';
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: 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> {
#override
Widget build(BuildContext context) {
print('MainPage: build');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextDisplay(
name: 'MainPage TextDisplay',
),
SizedBox(
height: 20,
),
RaisedButton(
child: Text('Open New Page'),
onPressed: () => Navigator.of(context).push(MaterialPageRoute(
builder: (context) => NewPage(),
)),
),
],
),
),
);
}
}
class TextDisplay extends StatefulWidget {
final String name;
const TextDisplay({Key key, #required this.name}) : super(key: key);
#override
_TextDisplayState createState() => _TextDisplayState();
}
class _TextDisplayState extends State<TextDisplay> {
/// this holds the latest known status of the widget's visibility
/// if [true] then the widget is fully visible, otherwise it is false.
///
/// Note: it is also [false] if the widget is partially visible since we are
/// only checking if the widget is fully visible or not
bool _isVisible = true;
#override
Widget build(BuildContext context) {
return Container(
child: ChangeNotifierProvider.value(
value: dataHolder,
/// This is the widget which identifies if the widget is visible or not
/// To my suprise this is an external plugin which is developed by Google devs
/// for the exact same purpose
child: VisibilityDetector(
key: ValueKey<String>(widget.name),
onVisibilityChanged: (info) {
// print('\n ------> Visibility info:'
// '\n name: ${widget.name}'
// '\n visibleBounds: ${info.visibleBounds}'
// '\n visibleFraction: ${info.visibleFraction}'
// '\n size: ${info.size}');
/// We use this fraction value to determine if the TextDisplay widget is
/// fully visible or not
/// range for fractional value is: 0 <= visibleFraction <= 1
///
/// Meaning we can also use fractional values like, 0.25, 0.3 or 0.5 to
/// find if the widget is 25%, 30% or 50% visible on screen
_isVisible = info.visibleFraction == 1;
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(child: Text(widget.name)),
SizedBox(
height: 20,
),
Consumer<DataHolder>(
builder: (context, holder, child) {
/// now that we have the status of the widget's visiblity
/// we can skip the long process when the widget is not visible.
if (_isVisible) {
doLongProcess(widget.name);
}
return Text(holder.data);
},
),
RaisedButton(
child: Text('Randomize'),
onPressed: () => randomizeData(),
),
],
),
),
),
);
}
void doLongProcess(String name) {
print('\n ============================ \n');
print('$name: '
'Doing a long process using the new data, isMounted: $mounted');
final element = widget.createElement();
print('\n name: ${widget.name}'
'\n element: $element'
'\n owner: ${element.state.context.owner}');
print('\n ============================ \n');
}
}
class NewPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
print('NewPage: build');
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: true,
title: Text('New Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextDisplay(
name: 'NewPage TextDisplay',
),
],
),
),
);
}
}
/////////////////// Data Holder Class and methods ///////////////////
class DataHolder extends ChangeNotifier {
String _data;
String get data => _data ?? 'Nothing to show, Yet!';
setData(String newData) {
print('\n new data found: $newData');
_data = newData;
notifyListeners();
}
}
final dataHolder = DataHolder();
randomizeData() {
int mills = DateTime.now().millisecondsSinceEpoch;
dataHolder.setData(mills.toString());
}

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

How to implement a right navbar in flutter?

The flutter scaffold shows a right navbar, but I suppose there is no right nav widget. How do I implement a right navbar with scaffold in flutter?
Flutter Scaffold Image
The Scaffold now has a endDrawer property which swipes from right-to-left.
Hope this might help someone.
If you are trying to show a right bar/menu or Drawer in your app, whether it is has a permanent view or a temporary one. I was able to achieve this by building my own custom widget from Allign, Container and Column widgets, and by using setState to show or hide the menu bar based on user interaction, see this simple example.
My custom menu widget looks like the following:
class RightNavigationBar extends StatefulWidget {
#override
_RightNavigationBarState createState() => new _RightNavigationBarState();
}
class _RightNavigationBarState extends State<RightNavigationBar> {
#override
Widget build(BuildContext context) {
return new Align(
alignment: FractionalOffset.centerRight,
child: new Container(
child: new Column(
children: <Widget>[
new Icon(Icons.navigate_next),
new Icon(Icons.close),
new Text ("More items..")
],
),
color: Colors.blueGrey,
height: 700.0,
width: 200.0,
),
);
}
}
Then when the user presses the menu icon, an object of my custom RightNavigationBar widget is created inside setState :
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
var _myRightBar = null;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
actions: [new IconButton(
icon: new Icon (Icons.menu), onPressed: _showRightBar)
],
title: new Text("Right Navigation Bar Example"),
),
body: _myRightBar
);
}
_showRightBar() {
setState(() {
_myRightBar == null
? _myRightBar = new RightNavigationBar()
: _myRightBar = null;
});
}
}
vertical_navigation_bar
How to use it? #
Install
dependencies:
vertical_navigation_bar: ^0.0.1
Run flutter command
flutter pub get
import 'package:flutter/material.dart';
import 'package:vertical_navigation_bar/vertical_navigation_bar.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Abubakr Elghazawy (Software Developer)',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final pageController = PageController(
initialPage: 0,
keepPage: true
);
final navItems = [
SideNavigationItem(icon: FontAwesomeIcons.calendarCheck, title: "New task"),
SideNavigationItem(icon: FontAwesomeIcons.calendarAlt, title: "Personal task"),
SideNavigationItem(icon: FontAwesomeIcons.fileAlt, title: "Personal document"),
SideNavigationItem(icon: FontAwesomeIcons.calendar, title: "Company task"),
SideNavigationItem(icon: FontAwesomeIcons.arrowCircleRight, title: "Options")
];
final initialTab = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: <Widget>[
SideNavigation(
navItems: this.navItems,
itemSelected: (index){
pageController.animateToPage(
index,
duration: Duration(milliseconds: 300),
curve: Curves.linear
);
},
initialIndex: 0,
actions: <Widget>[
],
),
Expanded(
child: PageView.builder(
itemCount: 5,
controller: pageController,
scrollDirection: Axis.vertical,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index){
return Container(
color: Colors.blueGrey.withOpacity(0.1),
child: Center(
child: Text("Page " + index.toString()),
)
);
},
),
)
],
),
);
}
}
More Dtailsenter link description here
Use the endDrawer property of the Scaffold like this:
Scaffold(
resizeToAvoidBottomInset: false,
key: _scaffoldKey,
endDrawer: const SideBar(),
body: CustomScrollView(