I tried to create listview using bloc.
this is my bloc class
MainPageList model looks like this
class MainPageList { List items; List icons;
MainPageList(this.items, this.icons); }
class MainPageBloc extends BlocBase {
MainPageList _mainPageList;
StreamController<MainPageList> _mainPageController =
StreamController<MainPageList>.broadcast();
StreamSink<MainPageList> get _inMainPage => _mainPageController.sink;
Stream<MainPageList> get outMainPage => _mainPageController.stream;
MainPageBloc(context) {
init(context);
}
void init(BuildContext context) async {
_mainPageList.items = [
AppTranslations.of(context).text("submit_request"),
AppTranslations.of(context).text("signout")
];
_mainPageList.icons = [
"lib/assets/images/submit_req.svg",
"lib/assets/images/sign_out.svg"
];
_inMainPage.add(_mainPageList);
}
#override
void dispose() {
_mainPageController.close();
}
}
In widget class I tried to call bloc.
#override
Widget build(BuildContext context) {
final MainPageBloc mainBloc = BlocProvider.of<MainPageBloc>(context);
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
body: StreamBuilder<MainPageList>(
assign stream,
stream: mainBloc.outMainPage,
builder: (context, snapshot) {
Container(
child: Scaffold(
body: Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.items.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Container(
child: ListTile(
contentPadding: EdgeInsets.symmetric(
horizontal: 5.0, vertical: 0.0),
assign to icons
leading: Container(
child: SvgPicture.asset(
snapshot.data.icons[index],
width: 40.0,
color: const Color(0xFFE27023),
),
),
assign to items
title: Text(
snapshot.data.items[index],
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold),
),
You are not providing enough code to know the context of your build method but it seems that you are using a BlocProvider. You should know that in order to have the bloc in the context of your widget you have to call it this way:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: BlocProvider<MyBloc>(
bloc: MyBloc(),
child: MyHome(),
),
);
}
}
class MyHome extends StatelessWidget{
#override
Widget build(BuildContext context) {
myBloc = BlocProvider.of<MyBloc>(context);
return Container();
}
}
As you can see, home of my app is not directly MyHome but a BlocProvider widget.
Related
The below code navigates to the respective services page (example: 'HomeDeliveryScreen', 'LaundryServiceScreen', 'PlumbingServiceScreen').
GridTile(
footer: ElevatedButton(
onPressed: () {
// Navigate to it's dedicated pages
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ServiceDetailScreen(
service: services[index],
),
),
);
},
);
How can we get these respective service pages ('HomeDeliveryScreen', 'LaundryServiceScreen', 'PlumbingServiceScreen') dynamically? as in my case, each service is a different screen as mentioned above instead of the below line.
class ServiceDetailScreen extends StatelessWidget {
const ServiceDetailScreen({Key? key, required this.service})
: super(key: key);
final Service service;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(service.categoryName),
actions: [
IconButton(
onPressed: () {},
icon: Icon(Icons.notifications),
),
],
),
body: Container(
// ******************************************
// How can we get these respective service pages ('HomeDeliveryScreen', 'LaundryServiceScreen', 'PlumbingServiceScreen') dynamically? as in my case, each service is a different screen as mentioned above instead of the below line.
// ******************************************
child: HomeDeliveryScreen(),
),
);
}
}
use GridView.builder also you can use data.asMap().map(....)
GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
childAspectRatio: 3 / 2,
crossAxisSpacing: 20,
mainAxisSpacing: 20),
itemCount: widgets.length,
itemBuilder: (BuildContext ctx, index) {
final widget = widgets[index];
final title = titles[index];
return Column(children: [
Text(title),
Image.asset('yourImage'),
ElevatedButton(
child: Text("to page"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => widget,
),
);
},
),
]);
})
full code
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: Colors.white,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
final List<Widget> widgets = [HomeDeliveryScreen(), LaundryServiceScreen(), PlumbingServiceScreen()];
final List<String> titles = ['first', 'second', 'third'];
#override
Widget build(BuildContext context) {
return Scaffold(body: GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
childAspectRatio: 3 / 2,
crossAxisSpacing: 20,
mainAxisSpacing: 20),
itemCount: widgets.length,
itemBuilder: (BuildContext ctx, index) {
final widget = widgets[index];
final title = titles[index];
return Column(children: [
Text(title),
Image.asset('yourImage'),
ElevatedButton(
child: Text("to page"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => widget,
),
);
},
),
]);
}));
}
}
class HomeDeliveryScreen extends StatelessWidget {
Widget build(BuildContext context) {
return Text('HomeDeliveryScreen');
}
}
class LaundryServiceScreen extends StatelessWidget {
Widget build(BuildContext context) {
return Text('LaundryServiceScreen');
}
}
class PlumbingServiceScreen extends StatelessWidget {
Widget build(BuildContext context) {
return Text('PlumbingServiceScreen');
}
}
Navigator.push(context,MaterialpageRoute(builder:(context)=>ServiceDetailScreen(
service: services[index])));
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:
),
);
},
),
),
);
}
}
I'm trying to write a small application in which I collect data through api. I take the data, everything works. I decided to make a navigation bar to switch between pages. But when I try on the pages they are empty. In order for the data to be updated on the page, I need to click "Hot reload". I will be grateful for your help.
My main.dart:
import 'package:flutter/material.dart';
import 'package:flutter_app_seals/model/dataArea_list/JsonDataArea.dart';
import 'package:flutter_app_seals/model/object_list/JsonObject.dart';
import 'package:flutter_app_seals/model/seals_list/JsonSeals.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new HomeScreen());
}
}
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Журнал пломби'),
),
// body: Seals(),
drawer: Drawer(
child: ListView(
children: <Widget>[
ListTile(
title: Text("Seals List"),
trailing: Icon(Icons.arrow_back),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Seals()),
);
}
)
],
),
),
);
}
}
class Seals extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home:JsonParseSeals(),
);
}
}
My modul Seals:
import 'package:flutter/material.dart';
import 'package:flutter_app_seals/model/seals_list/SealsListGet.dart';
import 'package:flutter_app_seals/model/seals_list/ServicesSeals.dart';
class JsonParseSeals extends StatefulWidget {
//
JsonParseSeals() : super();
#override
_JsonParseSealsState createState() => _JsonParseSealsState();
}
class _JsonParseSealsState extends State <StatefulWidget> {
//
List<SealList> _seals;
bool _loading;
#override
void initState(){
super.initState();
_loading = true;
Services.getSeals().then((seals) {
_seals =seals;
_loading = false;
}
);
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Список пломби'),
),
body: ListView.builder(
physics: BouncingScrollPhysics(),
padding: EdgeInsets.all(40),
itemCount: null == _seals ? 0 :_seals.length,
itemBuilder: (_,index) => Card(
color: Colors.red[300],
margin: EdgeInsets.symmetric(vertical: 7),
child:ListTile(
title: Text(_seals[index].sealNumber,
style: TextStyle(fontSize: 30),
),
subtitle: Text(
"${_seals[index].used}" ),
leading: Icon(Icons.local_activity,
size: 40,
color: Colors.black87,
),
),
),
),
);
}
}
My code :
Code after change:
Try to wrap your screen with data in FutureBuilder (you can read more about this widget here):
class _JsonParseSealsState extends State <StatefulWidget> {
#override
Widget build(BuildContext context) {
return FutureBuilder<List<SealList>>(
future: Services.getSeals(),
builder: (context, snapshot) {
// Data is loading, you should show progress indicator to a user
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
// Data is loaded, handle it
return ListView.builder(
physics: BouncingScrollPhysics(),
padding: EdgeInsets.all(40),
itemCount: snapshot.data.length,
itemBuilder: (_, index) {
final item = snapshot.data[index];
return Card(
color: Colors.red[300],
margin: EdgeInsets.symmetric(vertical: 7),
child: ListTile(
title: Text(
item.sealNumber,
style: TextStyle(fontSize: 30),
),
subtitle: Text("${item.used}"),
leading: Icon(
Icons.local_activity,
size: 40,
color: Colors.black87,
),
),
);
},
),
}
);
}
}
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(),
);
}
}
This is probably a really easy-to-fix question, but I figured that I might as well ask it here anyways: Is there any way to anchor a widget within a CustomScrollView? I want to use the CustomScrollView to support a flexible space in the app bar, but I need to have an input widget stay fixed at the bottom of the screen. I tried nesting the CustomScrollView into a Column with the given widget, but it doesn't seem to be working:
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
CustomScrollView(
slivers: <Widget>[
_buildAppBar(), // Returns a SliverAppBar
_buildMessages(), // Returns a StreamBuilder that returns a SliverList
],
),
MessageInputWidget(), // Input that needs to stay fixed
],
);
}
And here's that _buildMessages() method:
Widget _buildMessages(BuildContext context) {
return StreamBuilder<List<Message>>(
stream: widget.classroom.messages(),
builder: (context, snapshot) {
print('[DEBUG] Building chat with updated message stream...');
if (!snapshot.hasData || snapshot.data == null) {
return Center(
child: CircularProgressIndicator(),
);
}
_messages = snapshot.data;
print('[DEBUG] Building ${_messages.length} messages...');
return SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
if (index == _messages.length) {
return _buildHeader(context);
}
return MessageWidget(
message: _messages[index],
isReceived: _user.id != _messages[index].sentBy.id,
showUser: (index ==
0) || // Show _avatar if it's the first msg
(index >=
1 && // Or if it's a different _user than the last
!(_messages[index].sentBy.id ==
_messages[index - 1].sentBy.id)),
);
},
childCount: _messages.length,
),
);
});
}
Any suggestions? I've found some examples but that builds the whole CustomScrollView while I only want to build the SliverList whenever I get a new snapshot.
Any suggestions?
Yes, but for that you don't put it in the custom scroll, you use a stack widget. It puts the layered widget on the screen. What comes below you put before. In order for your widget to be positioned at the bottom, you must use a column with an expanded.
Stack(
children: <Widget>[
yourStreamBuilder(),
Column(
children: <Widget>[
Expanded(child: Container()),
Container(
width: MediaQuery.of(context).size.width,
height: 44,
color: Colors.red,
child: Text("Your bottom container"),
)
],
),
],
)
Complete exemple:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#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> {
Map<String, dynamic> tocando = {};
String ultimoTocando;
Future<List<Map<String, dynamic>>> listaDeMusicas() async {
return List<Map<String, dynamic>>.generate(
1200,
(i) => {"audio": "musica $i", "idUnico": "$i"},
);
}
tocar(String musica) {
print("tocando $musica ");
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Stack(
children: <Widget>[
yourStreamBuilder(),
Column(
children: <Widget>[
Expanded(child: Container()),
Container(
width: MediaQuery.of(context).size.width,
height: 44,
color: Colors.red,
child: Text("Your bottom container"),
)
],
),
],
));
}
Widget yourStreamBuilder() {
return FutureBuilder<List<Map<String, dynamic>>>(
future: listaDeMusicas(),
initialData: [],
builder: (context, snapshot) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
var item = snapshot.data[index];
if (tocando[item["idUnico"]] == null)
tocando[item["idUnico"]] = false;
return Row(
children: <Widget>[
IconButton(
icon: Icon(
tocando[item["idUnico"]] ? Icons.stop : Icons.play_arrow),
onPressed: () {
setState(() {
if (ultimoTocando != null) {
tocando[ultimoTocando] = false;
}
if (ultimoTocando != item["idUnico"]) {
tocando[item["idUnico"]] = !tocando[item["idUnico"]];
}
if (tocando[item["idUnico"]]) {
tocar(item["audio"]);
}
});
ultimoTocando = item["idUnico"];
},
),
Text(item["audio"]),
],
);
},
);
},
);
}
}