Can anyone show how to make this?
You can use this code.
class _HomePageState extends State<HomePage> {
List<String> _list = List.generate(5, (i) => "${i}");
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ReorderableListView(
padding: EdgeInsets.symmetric(horizontal: 40),
children: _list.map((String string) => ListTile(key: Key(_list[_list.indexOf(string)]), title: Text(string))).toList(),
onReorder: (oldIndex, newIndex) {
String old = _list[oldIndex];
if (oldIndex > newIndex) {
for (int i = oldIndex; i > newIndex; i--) {
_list[i] = _list[i - 1];
}
_list[newIndex] = old;
} else {
for (int i = oldIndex; i < newIndex - 1; i++) {
_list[i] = _list[i + 1];
}
_list[newIndex - 1] = old;
}
setState(() {});
},
),
);
}
}
Another option, instead of directly adjusting index values:
if (oldIndex < newIndex) {
newIndex -= 1;
}
final int item = _items.removeAt(oldIndex);
_items.insert(newIndex, item);
As can be seen in an example on https://api.flutter.dev/flutter/material/ReorderableListView-class.html.
Was trying to do this myself today. Was not satisfied with either answer. Here is what I came up with.
ReorderableListView(
padding: EdgeInsets.all(6),
buildDefaultDragHandles: false,
onReorder: (int oldIndex, int newIndex) {
test.insert(newIndex, test[oldIndex]);
if (oldIndex < newIndex) {
test.removeAt(oldIndex);
} else {
test.removeAt(oldIndex + 1);
}
setState(() {});
},
children: test.asMap().entries.map((e) {
return ReorderableDragStartListener(
index: e.key,
key: Key(e.key.toString() + e.value),
child: AspectRatio(
aspectRatio: 16 / 9,
child: Card(
child: Center(child: Text(e.value)),
elevation: 4,
),
),
);
}).toList(),
),
It this avoids the unneeded loops in user6274128's answer, and doesn't have the bug in Scott's answer. It also avoids bugs with index being based on the List.indexof as that will return the first instance of the String instead of the correct one.
Related
I'm facing some issues quite the time lately. I'm using the scoped_model package for my application. But i'm getting the problem that when i'm searching for stuff or i turn my screen to landscape modus it builds twice.
When this happens i'm getting duplicate json because it is rebuilding twice.
I'm getting another issue when i have searched and i go back my custom scroll view is not working anymore. I can't scroll down anymore.
I have writtin the following files:
main.dart:
class GetRelations extends StatefulWidget {
#override
RelationsPage createState() => RelationsPage();
}
class RelationsPage extends State<GetRelations> {
final RelationScopedModel relationScopedModel = RelationScopedModel();
#override
Widget build(BuildContext context) {
relationScopedModel.initializeValues();
return Scaffold(
body: ScopedModel<RelationScopedModel>(
model: relationScopedModel,
child: Container(
child: SearchScreen(),
),
),
);
}
}
search_screen.dart:
class SearchScreen extends StatefulWidget {
#override
SearchScreenState createState() {
return new SearchScreenState();
}
}
class SearchScreenState extends State<SearchScreen> {
final RelationScopedModel relationScopedModel = RelationScopedModel();
ScrollController controller;
int page = 0;
bool atEnd = false;
#override
void initState() {
super.initState();
controller = new ScrollController()..addListener(_scrollListener);
}
#override
void dispose() {
super.dispose();
controller.dispose();
}
void _scrollListener() {
var props = RelationScopedModel.of(context);
/// When reload we check if skip == 0, if skip is 0 then page has to become 0 too.
if (props.skip == 0) {
page = 0;
}
/// Checking if user is at the end of the screen, then let's receive some new content.
if (controller.position.pixels == controller.position.maxScrollExtent) {
/// If it is at the end, we have to set atEnd to true.
if (props.atTheEnd == true) {
atEnd = props.atTheEnd;
return;
}
/// If it has no more pages, return.
if (props.hasMorePages == false) {
return;
}
/// If it is has more stuff, load it in!
if (!props.isLoadingMore && props.hasMorePages) {
page++;
props.getRelations(props.search, page);
}
}
}
#override
Widget build(BuildContext context) {
/// Go back to last page by using WillPopScope.
return WillPopScope(
onWillPop: () {
Navigator.pop(context);
},
child: new Scaffold(
drawer: new DrawerOnly(),
/// We are using Scoped Model to load the json in the sliver list.
body: ScopedModelDescendant<RelationScopedModel>(
builder: (context, child, model) {
return CustomScrollView(
controller: controller,
slivers: <Widget>[
SliverAppBar(
title: SearchWidget(
performSearch: model.getRelations,
),
floating: true,
pinned: true,
),
model.isLoading && model.atTheEnd
? SliverFillRemaining(
child: Center(
child: CircularProgressIndicator(),
),
)
: model.getRelationCount() < 1
? SliverFillRemaining(
child: Center(
child: Text(
model.statusText,
style: Theme.of(context).textTheme.headline,
),
),
)
: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index == model.getRelationCount() + 1) {
if (model.hasMorePages == true) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 16.0),
child: Center(
child: CircularProgressIndicator()),
);
}
return Container(width: 0, height: 0);
} else if (index == 0) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.grey[300]))),
child: Text(
"Relaties",
style: Theme.of(context)
.textTheme
.body2
.copyWith(color: Colors.white),
),
);
} else {
return Container(
child: Column(
children: <Widget>[
InkWell(
child: RelationItem(
model.relation[index - 1]),
onTap: () {
var id =
model.relation[index - 1].id;
Navigator.of(context).push(
new MaterialPageRoute(
builder: (BuildContext
context) =>
new DetailScreen(id)));
},
),
],
),
);
}
},
childCount: model.getRelationCount() + 2,
),
)
],
);
},
),
),
);
}
}
search.dart (this is my searchwidget):
class SearchWidget extends StatelessWidget { final performSearch;
const SearchWidget({Key key, #required this.performSearch}) : super(key: key);
#override Widget build(BuildContext context) {
print('wat gebeurt er');
return Card(
elevation: 3.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(2.0)),
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
GestureDetector(
child: Icon(
Icons.search,
color: Colors.white,
),
onTap: () {},
),
SizedBox(
width: 10.0,
),
Expanded(
child: TextField(
decoration: InputDecoration(
hintStyle: TextStyle(fontSize: 14.0, color: Colors.white),
border: InputBorder.none,
hintText: "Zoeken..."),
onChanged: (String search) {
if (search.length > 2) {
performSearch(search);
}
if (search.length == 0) {
performSearch(search);
}
},
),
),
InkWell(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => SettingsScreen()));
},
child: Icon(
FontAwesomeIcons.slidersH,
color: Colors.white,
),
),
],
),
),
); } }
relation_scoped_model.dart:
class RelationScopedModel extends Model {
List<GetRelation> _relation = [];
List<GetContacts> _contacts = [];
List<GetLocations> _locations = [];
bool _isLoading = false;
String _statusText = "Start met zoeken...";
bool _hasMorePages = true;
int _skip = 0;
int skipMore = 0;
String _search;
bool _isLoadingMore = false;
bool atTheEnd = false;
List<Map<String, String>> _listingTypeList = [
{"name": "Buy", "value": "buy"},
{"name": "Rent", "value": "rent"},
];
String _listingType;
List<Map<String, String>> _sortList = [
{"name": "Relevancy", "value": "relevancy"},
{"name": "Bedroom (Ascending)", "value": "bedroom_lowhigh"},
{"name": "Bedroom (Descending)", "value": "bedroom_highlow"},
{"name": "Price (Ascending)", "value": "price_lowhigh"},
{"name": "Price (Descending)", "value": "price_highlow"},
{"name": "Newest", "value": "newest"},
{"name": "Oldest", "value": "oldest"},
{"name": "Random", "value": "random"},
{"name": "Distance", "value": "distance"}
];
String _sort;
List<GetRelation> get relation => _relation;
List<GetContacts> get contacts => _contacts;
List<GetLocations> get locations => _locations;
bool get isLoading => _isLoading;
String get statusText => _statusText;
bool get hasMorePages => _hasMorePages;
String get search => _search;
int get skip => _skip;
bool get isLoadingMore => _isLoadingMore;
List<Map<String, String>> get listingTypeList => _listingTypeList;
String get listingType => _listingType;
List<Map<String, String>> get sortList => _sortList;
String get sort => _sort;
int getRelationCount() => _relation.length;
void initializeValues() async {
_relation.clear();
SharedPreferences prefs = await SharedPreferences.getInstance();
_listingType = prefs.getString('listingType') ?? 'rent';
_sort = prefs.getString('sort') ?? 'relevancy';
getRelations(search);
}
var _skiptotal = 10;
var lastSearch;
Future<dynamic> _getData(String search, [int page = 0]) async {
/// When page is 0 we don't have to skip content, if page is 1 or higher it will have to skip content
if (page != 0) {
var skipPage = page * _skiptotal;
_skip = skipPage;
}
/// If page == 0, we have to set page and _skip to 0.
else {
_skip = 0;
page = 0;
}
/// When nothing is filled in the input search, we have to set search to single quotes because otherwise it will search on null.
if (search == null) {
search = '';
}
if (lastSearch != search) {
_relation.clear();
lastSearch = '';
lastSearch = search;
page + 1;
}
String _credentials;
SharedPreferences pref = await SharedPreferences.getInstance();
_credentials = (pref.getString("credentials") ?? "Empty");
var res = await http.get(
Uri.encodeFull("$cf_api_RelationsUrl" +
"?$cf_api_SkipParameter=$_skip&$cf_api_SearchParameter=$search&$cf_api_LimitParameter=10"),
headers: {
"content-type": "application/json",
"accept": "application/json",
'cookie': '$_credentials'
});
var decodedJson = json.decode(res.body, reviver: (k, v) {
if (k == "status" && v == false) {
_hasMorePages = false;
return v;
} else {
return v;
}
});
if (_hasMorePages == false) {
return decodedJson;
} else {
List list = List();
list = json.decode(res.body) as List;
if (list.length < 10) {
atTheEnd = true;
_hasMorePages = false;
return decodedJson;
}
}
return decodedJson;
}
Future getRelations(String search, [int page = 0]) async {
if (page == 0) {
page = 0;
_isLoading = true;
_relation.clear();
} else {
_isLoadingMore = true;
}
_search = search;
var responseData = await _getData(search, page);
notifyListeners();
var result = responseData
.map(
(data) => serializers.deserializeWith(GetRelation.serializer, data))
.toList();
result.forEach((relations) {
_relation.add(relations);
});
if (result.isEmpty) {
_statusText = "Nothing Found";
}
if (page == 0) {
_isLoading = false;
} else {
_isLoadingMore = false;
}
if (atTheEnd == true) {
return true;
}
// notifyListeners();
}
void setListingType(String value) async {
_listingType = value;
getRelations(search);
notifyListeners();
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('listingType', _listingType);
}
void setSort(String value) async {
_sort = value;
getRelations(search);
notifyListeners();
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('sort', _sort);
}
/// Wraps [ScopedModel.of] for this [Model].
static RelationScopedModel of(BuildContext context) =>
ScopedModel.of<RelationScopedModel>(context);
}
I'm guessing that the problem is that my scopedmodeldescendant isn't in the init state. But i'm not sure and i hope somebody knows what the problem is with this code.
Thanks in advance
Greetings,
Jente
UPDATE:
I got my customscrollview is working when i set atTheEnd to false and hasMorePages to true in my relation_scoped_model at initializeValues().
The only problem i'm still facing is that i'm getting duplicates, which happens because my screen is rebuilding twice.
When using scoped model, you should wrap the widgets that should respond to changes one by one with scoped model descendants. This way only those widgets can rebuild rather than the whole page rebuilding.
I have a PageView used with a BottomNavigationBar so that I can have swipeable and tappable tabs with a bottom bar rather than the normal navigation bar. Then I have two tabs/pages you can swipe between. One has a form with 4 UI elements and the other has no UI elements yet. Everything works fine but the performance of the PageView is very bad.
When I swipe between pages it is extremely slow and jumpy at first, definitely not the 60 frames per second promised by Flutter. Probably not even 30. After swiping several times though the performance gets better and better until its almost like a normal native app.
Below is my page class that includes the PageView, BottomNavigationBar, and logic connecting them. does anyone know how I can improve the performance of the PageView?
class _TabbarPageState extends State<TabbarPage> {
int _index = 0;
final controller = PageController(
initialPage: 0,
keepPage: true,
);
final _tabPages = <Widget>[
StartPage(),
OtherPage(),
];
final _tabs = <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.play_arrow),
title: Text('Start'),
),
BottomNavigationBarItem(
icon: Icon(Icons.accessibility_new),
title: Text('Other'),
)
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: PageView(
controller: controller,
children: _tabPages,
onPageChanged: _onPageChanged,
),
bottomNavigationBar: BottomNavigationBar(
items: _tabs,
onTap: _onTabTapped,
currentIndex: _index,
),
floatingActionButton: _index != 1
? null
: FloatingActionButton(
onPressed: () {},
tooltip: 'Test',
child: Icon(Icons.add),
),
);
}
void _onTabTapped(int index) {
controller.animateToPage(
index,
duration: Duration(milliseconds: 300),
curve: Curves.ease,
);
setState(() {
_index = index;
});
}
void _onPageChanged(int index) {
setState(() {
_index = index;
});
}
}
Ensure you performance test with profile or release builds only. Evaluating performance with debug builds is completely meaningless.
https://flutter.io/docs/testing/ui-performance
https://flutter.io/docs/cookbook/testing/integration/profiling
Sorry but Günter's answer didn't helped me! You have to set physics: AlwaysScrollableScrollPhysics() And your performance increases.
Worked for me 👍
I am new to flutter, please tell me if this is wrong.
Have the same problem, here is my effort. Wors for me.
class HomePage extends StatefulWidget {
#override
State createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
var _currentIndex = 1;
var _pageController = PageController(initialPage: 1);
var _todoPage, _inProgressPage, _donePage, _userPage;
#override
void initState() {
this._currentIndex = 1;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageView.builder(
controller: this._pageController,
onPageChanged: (index) {
setState(() {
this._currentIndex = index.clamp(0, 3);
});
},
itemCount: 4,
itemBuilder: (context, index) {
if (index == 0) return this.todoPage();
if (index == 1) return this.inProgressPage();
if (index == 2) return this.donePage();
if (index == 3) return this.userPage();
return null;
},
),
bottomNavigationBar: buildBottomNavigationBar(),
);
}
Widget buildBottomNavigationBar() {
return BottomNavigationBar(
showUnselectedLabels: false,
items: [
BottomNavigationBarItem(title: Text("待办"), icon: Icon(Icons.assignment)),
BottomNavigationBarItem(title: Text("进行"), icon: Icon(Icons.blur_on)),
BottomNavigationBarItem(title: Text("完成"), icon: Icon(Icons.date_range)),
BottomNavigationBarItem(title: Text("我的"), icon: Icon(Icons.account_circle)),
],
currentIndex: this._currentIndex,
onTap: (index) {
setState(() {
this._currentIndex = index.clamp(0, 3);
});
_pageController.jumpToPage(this._currentIndex);
},
);
}
Widget todoPage() {
if (this._todoPage == null) this._todoPage = TodoPage();
return this._todoPage;
}
Widget inProgressPage() {
if (this._inProgressPage == null) this._inProgressPage = InProgressPage();
return this._inProgressPage;
}
Widget donePage() {
if (this._donePage == null) this._donePage = DonePage();
return this._donePage;
}
Widget userPage() {
if (this._userPage == null) this._userPage = UserPage();
return this._userPage;
}
}
I just cache the pages that pageview hold. this REALLY smooth my pageview a lot, like native. but would prevent hotreload (ref: How to deal with unwanted widget build?),.
Try this hack,
Apply viewportFraction to your controller with value 0.99(it can be 0.999 or 0.9999 hit and try until you get desired result)
final controller = PageController(
viewportFraction: 0.99 );
I think I have the design of my app wrong. I am new to flutter/dart and am finding myself confused by previous experience with other languages (specifically C# and JavaScript).
I have an app that currently consists of a 3 x 3 GridView of 9 colored circular tiles, named Tiled_Surface. Each tile is assigned the following onTap handler:
void on_tile_tapped(int index) {
setState(() {
tile_tapped = index;
});
} // on_tile_tapped
where index has an arbitrary value in the range [0..8). Whenever a tile is tapped, the color of the tile changes to a lighter value (actually the "accent color" of the tile's color). All of that works file.
The AppBar contains a title ("Tiled Surface Demo") and two actions that consist of two IconButtons (Icons.swap_horiz and Icons.replay). It is intended that when the swap icon is tapped that the tile colors are shuffled into a new random order. And when the replay icon is tapped the tile colors are restored to their original order. When the two AppBar icons are tapped there is no apparent change to the display until a tile is tapped. Then, the changes made by the AppBar taps are displayed.
This is not the desired effect. My problem is how to render Tiled_Surface when the AppBar icons are tapped.
The code for the app follows. Thanks for your thoughts.
// ignore_for_file: camel_case_types
// ignore_for_file: constant_identifier_names
// ignore_for_file: non_constant_identifier_names
import 'package:flutter/material.dart';
import 'dart:math';
const int NUMBER_TILES = 9;
final int cross_axis_count = (sqrt (NUMBER_TILES)).toInt();
final double cross_axis_spacing = 4.0;
final double main_axis_spacing = cross_axis_spacing;
List<int> indices = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
List normal_colors = [
Colors.red,
Colors.orange,
Colors.yellow,
Colors.green,
Colors.blue,
Colors.purple,
Colors.amber,
Colors.cyan,
Colors.indigo,
]; // normal_colors
List bright_colors = [
Colors.pinkAccent,
Colors.orangeAccent,
Colors.yellowAccent,
Colors.lightGreenAccent,
Colors.blue.shade200,
Colors.purpleAccent,
Colors.amberAccent,
Colors.cyanAccent,
Colors.indigoAccent,
]; // bright_colors
void reinitialize_tiles() {
indices.clear();
for (int i = 0; (i < NUMBER_TILES); i++) {
indices.add(i);
}
} // reinitialize_tiles
void randomize_tiles() {
var random = new Random();
indices.clear();
for (int i = 0; (i < NUMBER_TILES); i++) {
var varient = random.nextInt(9);
if (indices.length > 0) {
while (indices.contains(varient)) {
varient = random.nextInt(9);
}
}
indices.add(varient);
}
} // randomize_tiles
void main() => runApp(new MyApp());
class Tiled_Surface extends StatefulWidget {
Tiled_Surface({Key key}) : super(key: key);
#override // Tiled_Surface
Tiled_Surface_State createState() => Tiled_Surface_State();
}
class Tiled_Surface_State extends State<Tiled_Surface> {
List<GridTile> grid_tiles = <GridTile>[];
int tile_tapped = -1;
void on_tile_tapped(int index) {
setState(() {
tile_tapped = index;
});
} // on_tile_tapped
GridTile new_surface_tile(Color tile_color, int index) {
GridTile tile = GridTile(
child: GestureDetector(
onTap: () => on_tile_tapped(index),
child: Container(
decoration: BoxDecoration(
color: tile_color,
shape: BoxShape.circle,
),
),
)
);
return (tile);
} // new_surface_tile
List<GridTile> create_surface_tiles() {
grid_tiles = new List<GridTile>();
for (int i = 0; (i < NUMBER_TILES); i++) {
Color tile_color = ( tile_tapped == i ) ?
bright_colors[indices[i]] :
normal_colors[indices[i]];
grid_tiles.add(new_surface_tile(tile_color, i));
}
return (grid_tiles);
} // create_surface_tiles
#override // Tiled_Surface_State
Widget build(BuildContext context) {
return GridView.count(
shrinkWrap: true,
crossAxisCount: cross_axis_count,
childAspectRatio: 1.0,
padding: const EdgeInsets.all(4.0),
mainAxisSpacing: main_axis_spacing,
crossAxisSpacing: cross_axis_spacing,
children: create_surface_tiles(),
);
}
} // class Tiled_Surface_State
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Tiled Surface Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Tiled Surface Demo'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.swap_horiz),
onPressed: () {
randomize_tiles();
},
),
IconButton(
icon: Icon(Icons.replay),
onPressed: () {
reinitialize_tiles();
},
)
]
),
body: Column(
children: [
Tiled_Surface(),
],
),
),
);
}
}
Problem:
Flutter widgets(Stateful) will react to state variables only. Not for global and local. In your example indices is a global variable.
I updated the code with
Moved indices into MyApp as
Mutable global variables are not good
We want our MyApp to reflect for changes in indices
As MyApp started holding state changed it as StatefulWidget
Moved randomize_tiles and reinitialize_tiles into _MyAppState and added setState on change of indices so that widgets will get re-rendered.
As Tiled_Surface also need indices, injecting(passing) them in the constructor.
Please have a look
import 'package:flutter/material.dart';
import 'dart:math';
const int NUMBER_TILES = 9;
final int cross_axis_count = (sqrt(NUMBER_TILES)).toInt();
final double cross_axis_spacing = 4.0;
final double main_axis_spacing = cross_axis_spacing;
List normal_colors = [
Colors.red,
Colors.orange,
Colors.yellow,
Colors.green,
Colors.blue,
Colors.purple,
Colors.amber,
Colors.cyan,
Colors.indigo,
]; // normal_colors
List bright_colors = [
Colors.pinkAccent,
Colors.orangeAccent,
Colors.yellowAccent,
Colors.lightGreenAccent,
Colors.blue.shade200,
Colors.purpleAccent,
Colors.amberAccent,
Colors.cyanAccent,
Colors.indigoAccent,
]; // bright_colors
void main() => runApp(new MyApp());
class Tiled_Surface extends StatefulWidget {
List<int> indices;
Tiled_Surface(this.indices, {Key key}) : super(key: key);
#override // Tiled_Surface
Tiled_Surface_State createState() => Tiled_Surface_State(indices);
}
class Tiled_Surface_State extends State<Tiled_Surface> {
List<GridTile> grid_tiles = <GridTile>[];
int tile_tapped = -1;
List<int> indices;
Tiled_Surface_State(this.indices);
void on_tile_tapped(int index) {
setState(() {
tile_tapped = index;
});
} // on_tile_tapped
GridTile new_surface_tile(Color tile_color, int index) {
GridTile tile = GridTile(
child: GestureDetector(
onTap: () => on_tile_tapped(index),
child: Container(
decoration: BoxDecoration(
color: tile_color,
shape: BoxShape.circle,
),
),
));
return (tile);
} // new_surface_tile
List<GridTile> create_surface_tiles() {
grid_tiles = new List<GridTile>();
for (int i = 0; (i < NUMBER_TILES); i++) {
Color tile_color = (tile_tapped == i)
? bright_colors[indices[i]]
: normal_colors[indices[i]];
grid_tiles.add(new_surface_tile(tile_color, i));
}
return (grid_tiles);
} // create_surface_tiles
#override // Tiled_Surface_State
Widget build(BuildContext context) {
return GridView.count(
shrinkWrap: true,
crossAxisCount: cross_axis_count,
childAspectRatio: 1.0,
padding: const EdgeInsets.all(4.0),
mainAxisSpacing: main_axis_spacing,
crossAxisSpacing: cross_axis_spacing,
children: create_surface_tiles(),
);
}
} // class Tiled_Surface_State
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
List<int> indices = [0, 1, 2, 3, 4, 5, 6, 7, 8];
void randomize_tiles() {
var random = new Random();
indices.clear();
for (int i = 0; (i < NUMBER_TILES); i++) {
var varient = random.nextInt(9);
if (indices.length > 0) {
while (indices.contains(varient)) {
varient = random.nextInt(9);
}
}
indices.add(varient);
}
setState(() {});
}
void reinitialize_tiles() {
indices.clear();
for (int i = 0; (i < NUMBER_TILES); i++) {
indices.add(i);
}
setState(() {});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Tiled Surface Demo',
home: Scaffold(
appBar: AppBar(title: Text('Tiled Surface Demo'), actions: <Widget>[
IconButton(
icon: Icon(Icons.swap_horiz),
onPressed: () {
randomize_tiles();
},
),
IconButton(
icon: Icon(Icons.replay),
onPressed: () {
reinitialize_tiles();
},
)
]),
body: Column(
children: [
Tiled_Surface(indices),
],
),
),
);
}
}
Widget build(BuildContext context) {
TextField XnumField = new TextField(
keyboardType: TextInputType.numberWithOptions(),
decoration: new InputDecoration(labelText: "X array"),
onSubmitted: (String text){
for(i = 0; i < 4; i++){
X[i] = int.parse(text);
}
},
);
Hi,
If you are looking for something like this here you go.
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new DemoScreen(),
));
}
class DemoScreen extends StatefulWidget {
#override
_DemoScreenState createState() => new _DemoScreenState();
}
class _DemoScreenState extends State<DemoScreen> {
List<int> _myList = new List();
TextEditingController _myController = new TextEditingController();
String _result = "";
String _inputList = "";
setSum() {
int sum = 0;
for (int i = 0; i < _myList.length; i++) {
sum += _myList[i];
if (i == 0)
_inputList = "${_myList[i]}";
else
_inputList = _inputList + " + ${_myList[i]}";
}
_result = "$sum";
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Demo App"),
),
body: new Column(
children: <Widget>[
new Container(
margin: new EdgeInsets.symmetric(vertical: 10.0),
child: new Text(
_inputList,
style: new TextStyle(fontSize: 40.0),
),
),
new Container(
margin: new EdgeInsets.symmetric(vertical: 25.0),
child: new Text(
_result,
style: new TextStyle(fontSize: 70.0),
),
),
new Container(
margin: new EdgeInsets.symmetric(horizontal: 50.0),
child: new TextField(
controller: _myController,
keyboardType: TextInputType.number,
onSubmitted: (text) {
setState(() {
_myList.add(int.parse(text));
setSum();
_myController.clear();
});
},
),
)
],
),
);
}
}
Hope it helps :)
Something like ...
import 'dart:core';
List<int> BuildIntArray(String input) {
var outList = new List<int>();
final _delimiter = ',';
final _values = input.split(_delimiter);
_values.forEach((item) {
outList.add(int.parse(item));
});
return outList;
}
Which would ...
import 'package:IntegerArray/IntegerArray.dart' as IntegerArray;
main(List<String> arguments) {
final input = "1,2,3,4,5";
final intInputValues = IntegerArray.BuildIntArray(input);
print (intInputValues);
int sum = 0;
intInputValues.forEach((item) {
sum+=item;
});
print (sum);
}
... do this ...
Observatory listening on http://127.0.0.1:64499/
[1, 2, 3, 4, 5]
15
Process finished with exit code 0
I don't see a "tryParse" to filter out non-numeric values ... but with some validation/error checking you could add to this ...
So in my app I have a simple rating feature and in order to achieve this I've built a simple star widget which takes a 'fill' parameter 0.0-1.0 and the Rating widget uses a row of five stars counting the appropriate fill. The problem is that for some unexplainable reason the list (cuz it appears on a list) becomes so much slower when that specific widget is removed. Here's the code for he star:
class Star extends StatelessWidget {
final double fill;
final double scale;
Star({#required this.fill, this.scale = 1.0}):
assert(fill<=1.0 && fill >=0.0);
#override
Widget build(BuildContext context) {
return new Container(
margin: new EdgeInsets.only(
right: 2.5*scale,
),
child: new Material(
elevation: 2.0,
borderRadius: new BorderRadius.circular(3.0),
child: new Container(
height: 14.0*scale,
width: 14.0*scale,
child: new Stack(
children: <Widget>[
new Container(
color: Colors.blue,
width: this.fill * 14.0*scale,
),
new Center(
child: new Material(
elevation: 3.0,
color: new Color(0x00000000),
child: new Icon(
Icons.star,
size: 12.0*scale,
color: Colors.white,
),
),
),
],
),
),
),
);
}
}
And for the full rating widget:
class Rating extends StatelessWidget {
final double rating;
final double scale;
Rating({
#required this.rating,
this.scale = 1.0,
})
: assert(rating <= 5 && rating >= 0);
#override
Widget build(BuildContext context) {
final List<Star> stars = new List.generate(5, (index) {
return new Star(
fill: _getFill(index),
scale: scale,
);
});
return new Row(
children: stars,
);
}
int _getStarsNeeded() {
double r = rating;
if (r <= 0)
return 0;
else if (r > 0 && r <= 1)
return 1;
else if (r > 1 && r <= 2)
return 2;
else if (r > 2 && r <= 3)
return 3;
else if (r > 3 && r <= 4)
return 4;
else
return 5;
}
double _getEndingStarFill() {
return (rating + 1 - _getStarsNeeded());
}
double _getFill(index) {
int neededStars = _getStarsNeeded();
if (index < neededStars - 1) {
return 1.0;
} else if (index == neededStars - 1) {
return _getEndingStarFill();
} else
return 0.0;
}
}
I know this is not the best way to find the fill but that can't be the case here. And yet looks so simple to make things heavy.