How to use Streambuilder in flutter - android

import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
void main() async {
//Run this first
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Smart Bin',
home: new HomePageWidget(),
);
}
}
class HomePageWidget extends StatefulWidget {
const HomePageWidget({Key key}) : super(key: key);
#override
_HomePageWidgetState createState() => _HomePageWidgetState();
}
class _HomePageWidgetState extends State<HomePageWidget> {
final scaffoldKey = GlobalKey<ScaffoldState>();
final currentBinRecord = FirebaseFirestore.instance.collection("current_bin");
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
appBar: AppBar(
title: Text(
'SmartBin',
),
),
body: SafeArea(
child: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: StreamBuilder<List<CurrentBinRecord>>(
stream: queryCurrentBinRecord(
queryBuilder: (currentBinRecord) =>
currentBinRecord.orderBy('level', descending: true),
),
builder: (context, snapshot) {
// Customize what your widget looks like when it's loading.
if (!snapshot.hasData) {
return Center(
child: SizedBox(
width: 50,
height: 50,
child: CircularProgressIndicator(),
),
);
}
List<CurrentBinRecord> listViewCurrentBinRecordList =
snapshot.data;
return ListView.builder(
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
itemCount: listViewCurrentBinRecordList.length,
itemBuilder: (context, listViewIndex) {
final listViewCurrentBinRecord =
listViewCurrentBinRecordList[listViewIndex];
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
listViewCurrentBinRecord.area,
),
Text(
listViewCurrentBinRecord.level.toString(),
),
],
);
},
);
},
),
),
],
),
),
),
);
}
}
This is the error
First error is on:
child: StreamBuilder<List<CurrentBinRecord>>
The name 'CurrentBinRecord' isn't a type so it can't be used as a type argument.
Try correcting the name to an existing type, or defining a type named 'CurrentBinRecord'.
Second error is on:
stream: queryCurrentBinRecord
The method 'queryCurrentBinRecord' isn't defined for the type '_HomePageWidgetState'.
Try correcting the name to the name of an existing method, or defining a method named 'queryCurrentBinRecord'.
Third error is on:
List<CurrentBinRecord> listViewCurrentBinRecordList =
snapshot.data;
The name 'CurrentBinRecord' isn't a type so it can't be used as a type argument.
Try correcting the name to an existing type, or defining a type named 'CurrentBinRecord'.

These is the syntax try -
return StreamBuilder(
stream: theStreamSource, // Eg a firebase query
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, int index) {
return Text(snapshot.data.documents[index]['title']);
}
);
},
);
Hope it helps.

I think the best way to use StreamBuilder is to create separate controller class that can handle all your business logic and update UI.
// your widget class
class UIClass extends StatefulWidget {
const UIClass({Key key}) : super(key: key);
#override
_UIClassState createState() => _UIClassState();
}
class _UIClassState extends State<UIClass> {
UIClassController<List<CurrentBinRecord>> _uiController;
#override
void initState() {
_uiController = UIClassController(StreamController<List<CurrentBinRecord>>());
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Text("Hello Everyone"),
StreamBuilder<List<CurrentBinRecord>>(
stream: _uiController.uiStream,
builder: (context, snapshot) {
if(snapshot.hasError){
return ErrorWidget();
}
else if(snapshot.connectionState == ConnectionState.waiting){
return WaitingWidget();
}
else if(snapshot.hasData){
return ListView.builder(
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
itemCount: snapshot.data.length,
itemBuilder: (context, listViewIndex) {
final listViewCurrentBinRecord =
snapshot.data[listViewIndex];
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
listViewCurrentBinRecord.area,
),
Text(
listViewCurrentBinRecord.level.toString(),
),
],
);
},
);
}
else{
return SizedBox();
}
}
),
],
);
}
#override
void dispose() {
super.dispose();
_uiController.dispose();
}
}
// controller class to handle all business logic
// you can also split it into multiple sub controllers
class UIClassController<T> {
final StreamController<T> _controller;
// If you are using this on multiple widgets then use asBroadcastStream()
Stream<T> get uiStream => _controller.stream;
UIClassController(this._controller);
void updateMyUI([dynamic params]){
T t;
// your logic //
//------------//
_controller.sink.add(t);
}
void dispose(){
_controller.close();
}
}

Code I'm Using for StreamBuilder
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import '../door/widgets/Navbar.dart';
import '../door/widgets/sidenav.dart';
import 'package:get/get.dart';
FirebaseAuth auth = FirebaseAuth.instance;
class CartPage extends StatefulWidget {
#override
_CartPageState createState() => _CartPageState();
}
class _CartPageState extends State<CartPage> {
final FirebaseFirestore fb = FirebaseFirestore.instance;
int up = 1;
bool loading = false;
final ScrollController _scrollController = ScrollController();
#override
void initState() {
super.initState();
getCartData();
}
void addMore(qty, documentID) {
int new_qty = qty + 1;
Get.snackbar('Qty! ', new_qty.toString());
String collection_name = "cart_${auth.currentUser?.email}";
FirebaseFirestore.instance
.collection(collection_name)
.doc(documentID)
.update({
'qty': new_qty,
'updated_at': Timestamp.now(),
}) // <-- Your data
.then((_) => print('Added'))
.catchError((error) => Get.snackbar('Failed!', 'Error: $error'));
}
void minusMore(qty, documentID) {
if (qty > 1) {
int new_qty = qty - 1;
String collection_name = "cart_${auth.currentUser?.email}";
FirebaseFirestore.instance
.collection(collection_name)
.doc(documentID)
.update({
'qty': new_qty,
'updated_at': Timestamp.now(),
}) // <-- Your data
.then((_) => print('Subtracted'))
.catchError((error) => Get.snackbar('Failed!', 'Error: $error'));
}
}
Stream<QuerySnapshot> getCartData() {
String collection_name = "cart_${auth.currentUser?.email}";
return FirebaseFirestore.instance
.collection(collection_name)
.orderBy("created_at", descending: false)
.snapshots();
}
final ButtonStyle style = ElevatedButton.styleFrom(
minimumSize: Size(50, 30),
backgroundColor: Color(0xFFFFB61A),
elevation: 6,
textStyle: const TextStyle(fontSize: 11),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(20),
)));
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: Navbar.navbar(),
drawer: Sidenav.sidenav(),
body: Container(
padding: EdgeInsets.all(10.0),
child: StreamBuilder<QuerySnapshot>(
stream: getCartData(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
if (snapshot.hasError) {
return buildText('Something Went Wrong Try later');
} else {
if (!snapshot.hasData) {
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 100,
minHeight: 190,
maxWidth: 200,
maxHeight: 200,
),
child: Icon(Icons.shopping_cart, size: 45),
),
title: Text('No Food!'),
subtitle: const Text('Your cart is empty!'),
),
],
),
);
} else {
return ListView.builder(
physics: const BouncingScrollPhysics(),
shrinkWrap: true,
itemCount: snapshot.data?.docs.length,
itemBuilder: (BuildContext context, int index) {
return SizedBox(
height: 90.0,
child: Card(
elevation: 10,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
const SizedBox(
width: 20,
),
_buildImg('assets/logo.png', '60', '60'),
const SizedBox(
width: 14,
),
SizedBox(
width:
MediaQuery.of(context).size.width *
0.33,
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(
height: 20,
),
Text(
snapshot.data?.docs[index]
["name"],
maxLines: 2,
style: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 14)),
const SizedBox(
height: 5,
),
Text(
"₹${snapshot.data?.docs[index]["price"]}",
style: const TextStyle(
fontWeight: FontWeight.w400,
fontSize: 12)),
],
),
),
const SizedBox(
width: 10,
),
Container(
margin: const EdgeInsets.only(
top: 20.0, bottom: 10.0),
decoration: BoxDecoration(
color: const Color(0xFFFFB61A),
// color: Color(0xFF0A2031),
borderRadius:
BorderRadius.circular(17)),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment:
CrossAxisAlignment.end,
children: <Widget>[
SizedBox(
height: 40,
child: IconButton(
onPressed: () {
minusMore(
snapshot.data
?.docs[index]
["qty"],
snapshot
.data
?.docs[index]
.reference
.id);
},
color: Colors.white,
icon:
const Icon(Icons.remove)),
),
const SizedBox(
width: 5,
),
Container(
margin: const EdgeInsets.only(
bottom: 10.0),
child: Text(
"${snapshot.data?.docs[index]["qty"]}",
style: const TextStyle(
fontWeight:
FontWeight.w400,
color: Colors.white,
fontSize: 16)),
),
const SizedBox(
width: 5,
),
SizedBox(
height: 40,
child: IconButton(
color: Colors.white,
onPressed: () {
addMore(
snapshot.data
?.docs[index]
["qty"],
snapshot
.data
?.docs[index]
.reference
.id);
},
icon: const Icon(Icons.add)),
),
],
),
),
const SizedBox(
width: 10,
),
],
),
),
);
});
}
}
}
},
),
),
);
}
Widget buildText(String text) => Center(
child: Text(
text,
style: TextStyle(fontSize: 24, color: Colors.black),
),
);
_buildImg(img, hei, wid) {
return Container(
alignment: Alignment.center, // use aligment
child: Image.asset(
img,
height: double.parse(hei),
width: double.parse(wid),
fit: BoxFit.cover,
),
);
}
}

Related

If else statement not showing proper output in Flutter

I am following a course video from YouTube to design a shopping cart in Flutter. But according to the course video, I am supposed to see the output showing the pictures of the items, their prices, units, and other details [shown in picture 1]. But in my output I am seeing nothing but only the AppBar title [shown in picture 2].
The output in the YouTube course video:
My Output:
I have attached the entire code below, please help fix my mistake.
main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shopping_cart/cart_provider.dart';
import 'package:shopping_cart/cart_screen.dart';
import 'package:shopping_cart/product_list.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => CartProvider(),
child: Builder(builder: (BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
debugShowCheckedModeBanner: false,
home: const CartScreen(),
);
}),
);
}
}
product_list.dart
import 'package:badges/badges.dart';
import 'package:flutter/material.dart' hide Badge;
import 'package:provider/provider.dart';
import 'package:shopping_cart/cart_model.dart';
import 'package:shopping_cart/db_helper.dart';
import 'cart_provider.dart';
import 'cart_screen.dart';
class ProductListScreen extends StatefulWidget {
const ProductListScreen({super.key});
#override
State<ProductListScreen> createState() => _ProductListScreenState();
}
class _ProductListScreenState extends State<ProductListScreen> {
DBHelper? dbHelper = DBHelper();
List<String> productName = [
'Mango',
'Orange',
'Grapes',
'Banana',
'Chery',
'Peach',
'Mixed Fruit Basket'
];
List<String> productUnit = [
'KG',
'Dozen',
'KG',
'Dozen',
'KG',
'KG',
'KG',
];
List<int> productPrice = [10, 20, 30, 40, 50, 60, 70];
List<String> productImage = [
'https://image.shutterstock.com/image-photo/mango-isolated-on-white-background-600w-610892249.jpg',
'https://image.shutterstock.com/image-photo/orange-fruit-slices-leaves-isolated-600w-1386912362.jpg',
'https://image.shutterstock.com/image-photo/green-grape-leaves-isolated-on-600w-533487490.jpg',
'https://media.istockphoto.com/photos/banana-picture-id1184345169?s=612x612',
'https://media.istockphoto.com/photos/cherry-trio-with-stem-and-leaf-picture-id157428769?s=612x612',
'https://media.istockphoto.com/photos/single-whole-peach-fruit-with-leaf-and-slice-isolated-on-white-picture-id1151868959?s=612x612',
'https://media.istockphoto.com/photos/fruit-background-picture-id529664572?s=612x612',
];
#override
Widget build(BuildContext context) {
final cart = Provider.of<CartProvider>(context); // reference
return Scaffold(
appBar: AppBar(
title: const Text("Product List"),
centerTitle: true,
backgroundColor: Colors.deepPurple,
actions: [
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CartScreen(),
),
);
},
child: Center(
child: Badge(
badgeContent: Consumer<CartProvider>(
builder: (context, value, child) {
return Text(
value.getCounter().toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.bold,
),
);
},
),
badgeAnimation: const BadgeAnimation.fade(
animationDuration: Duration(milliseconds: 300),
),
child: const Icon(
Icons.shopping_cart_outlined,
size: 30,
),
),
),
),
const SizedBox(width: 20),
],
),
body: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: productName.length,
itemBuilder: ((context, index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Image(
height: 100,
width: 100,
image:
NetworkImage(productImage[index].toString()),
),
const SizedBox(
width: 15,
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
productName[index],
style: const TextStyle(fontSize: 23),
),
const SizedBox(
height: 5,
),
Text(
productUnit[index] +
" " r"$" +
productPrice[index].toString(),
style: const TextStyle(fontSize: 23),
),
const SizedBox(
height: 10,
),
Align(
alignment: Alignment.centerRight,
child: InkWell(
onTap: () {
dbHelper!
.insert(Cart(
id: index,
productId: index.toString(),
productName: productName[index]
.toString(),
initialPrice:
productPrice[index],
productPrice:
productPrice[index],
quantity: 1,
unitTag: productUnit[index]
.toString(),
image: productImage[index]
.toString()))
.then((value) {
print("Product is Added to Cart");
cart.addTotalPrice(double.parse(
productPrice[index].toString()));
cart.addCounter();
}).onError((error, stackTrace) {
print(error.toString());
});
},
child: Container(
height: 35,
width: 130,
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(50),
color: Colors.green,
),
child: const Center(
child: Text(
"Add to Cart",
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w500),
),
),
),
),
)
],
),
)
],
)
],
),
),
);
}),
),
),
Consumer<CartProvider>(builder: (context, value, child) {
return Column(
children: [
ReusableWidget(
'subtotal',
r'$' + value.getTotalPrice().toStringAsFixed(2),
),
],
);
})
],
),
);
}
}
class ReusableWidget extends StatelessWidget {
final String title, value;
const ReusableWidget(this.title, this.value, {super.key});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
Text(
title,
style: Theme.of(context).textTheme.titleSmall,
),
Text(
value,
style: Theme.of(context).textTheme.titleSmall,
)
],
),
);
}
}
db_helper.dart
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart';
import 'dart:io' as io;
import 'cart_model.dart';
class DBHelper {
static Database? _db;
Future<Database?> get db async {
_db = await initDatabase();
if (_db != null) {
return _db!;
}
}
initDatabase() async {
io.Directory documentDirectory = await getApplicationDocumentsDirectory();
String path = join(documentDirectory.path, 'cart.db');
var db = await openDatabase(path, version: 1, onCreate: _onCreate);
return db;
}
// Method
_onCreate(Database db, int version) async {
await db.execute(
'CREATE TABLE cart (id INTEGER PRIMARY KEY, productId VARCHAR UNIQUE, productName TEXT, initialPrice INTEGER, productPrice INTEGER, quantity INTEGER, unitTag TEXT, image TEXT)',
);
}
Future<Cart> insert(Cart cart) async {
print(cart.toMap());
var dbClient = await db;
await dbClient!.insert('cart', cart.toMap());
return cart;
}
Future<List<Cart>> getCartList() async {
var dbClient = await db;
final List<Map<String, Object?>> queryResult =
await dbClient!.query("cart");
return queryResult.map((e) => Cart.fromMap(e)).toList();
}
}
cart_screen.dart
import 'package:badges/badges.dart';
import 'package:flutter/material.dart' hide Badge;
import 'package:provider/provider.dart';
import 'cart_model.dart';
import 'cart_provider.dart';
class CartScreen extends StatefulWidget {
const CartScreen({super.key});
#override
State<CartScreen> createState() => _CartScreenState();
}
class _CartScreenState extends State<CartScreen> {
#override
Widget build(BuildContext context) {
final cart = Provider.of<CartProvider>(context);
return Scaffold(
appBar: AppBar(
title: const Text("My Products"),
centerTitle: true,
backgroundColor: Colors.deepPurple,
actions: [
Center(
child: Badge(
badgeContent: Consumer<CartProvider>(
builder: (context, value, child) {
return Text(
value.getCounter().toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.bold,
),
);
},
),
badgeAnimation: const BadgeAnimation.fade(
animationDuration: Duration(milliseconds: 300),
),
child: const Icon(
Icons.shopping_cart_outlined,
size: 30,
),
),
),
const SizedBox(width: 20),
],
),
body: Column(
children: [
FutureBuilder(
future: cart.getData(),
builder: (context, AsyncSnapshot<List<Cart>> snapshot) {
if (snapshot.hasData) {
return Expanded(
child: ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: ((context, index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Image(
height: 100,
width: 100,
image: NetworkImage(
snapshot.data![index].image.toString(),
),
),
const SizedBox(
width: 15,
),
Expanded(
child: Column(
mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
snapshot.data![index].productName
.toString(),
),
const SizedBox(
height: 5,
),
Text(
snapshot.data![index].unitTag
.toString() +
" " r"$" +
snapshot.data![index].productPrice
.toString(),
style: const TextStyle(fontSize: 23),
),
const SizedBox(
height: 10,
),
Align(
alignment: Alignment.centerRight,
child: InkWell(
onTap: () {},
child: Container(
height: 35,
width: 130,
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(50),
color: Colors.green,
),
child: const Center(
child: Text(
"Add to Cart",
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight:
FontWeight.w500),
),
),
),
),
)
],
),
)
],
)
],
),
),
);
}),
),
);
} else {
return const Text("Shahzain");
}
},
)
],
),
);
}
}
cart_provider.dart
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:shopping_cart/cart_model.dart';
import 'package:shopping_cart/db_helper.dart';
class CartProvider with ChangeNotifier {
DBHelper db = DBHelper(); // DBHelper() is a class
int _counter = 0;
int get counter => _counter;
double _totalPrice = 0.0;
double get totalPrice => _totalPrice;
late Future<List<Cart>> _cart;
Future<List<Cart>> get cart => _cart; // => means to indicate
Future<List<Cart>> getData() async {
_cart = db.getCartList();
return _cart;
}
void _setPrefItems() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setInt('cart_item', _counter);
prefs.setDouble('total_price', _totalPrice);
notifyListeners();
}
void _getPrefItems() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
// giving initial value by ?? 0 for null safety
_counter = prefs.getInt('cart_item') ?? 0;
// giving initial value by ?? 0.0 for null safety
_totalPrice = prefs.getDouble('total_price') ?? 0.0;
notifyListeners();
}
void addTotalPrice(double productPrice) {
_totalPrice = _totalPrice + productPrice;
_setPrefItems();
notifyListeners();
}
void removeTotalPrice(double productPrice) {
_totalPrice = _totalPrice - productPrice;
_setPrefItems();
notifyListeners();
}
double getTotalPrice() {
_getPrefItems();
return _totalPrice;
}
void addCounter() {
// initial value of counter was 0, will be incremented to 1, and will be stored to sharedPreferences
_counter++;
_setPrefItems();
notifyListeners();
}
void removeCounter() {
_counter--;
_setPrefItems();
notifyListeners();
}
int getCounter() {
_getPrefItems();
return _counter;
}
}
cart_model.dart
class Cart {
late final int? id;
final String? productId;
final String? productName;
final int? initialPrice;
final int? productPrice;
final int? quantity;
final String? unitTag;
final String? image;
// Creating the constructor
Cart({
required this.id, // primary key
required this.productId,
required this.productName,
required this.initialPrice,
required this.productPrice,
required this.quantity,
required this.unitTag,
required this.image,
});
Cart.fromMap(Map<dynamic, dynamic> res) // res = resources
: id = res['id'],
productId = res['productId'],
productName = res['productName'],
initialPrice = res['initialPrice'],
productPrice = res['productPrice'],
quantity = res['quantity'],
unitTag = res['unitTag'],
image = res['image'];
Map<String, Object?> toMap(){
return {
'id' : id,
'productId' : productId,
'productName' : productName,
'initialPrice' : initialPrice,
'productPrice' : productPrice,
'quantity' : quantity,
'unitTag' : unitTag,
'image' : image
};
}
}
in your main.dart file
you set,
home: const CartScreen(),
and the video tutorial you have followed, he sets home: ProductListsScreen()
Fix this and I hope your problem will be solved,
also, be sure where to use const

How to pass method into another class in flutter

I tried to create a task app using flutter, So I created a text field in DialogBox and my aim is when I add some text into the text field and when I clicked the OK button, I need to show that text in the list. but I have no idea how to call a method in another class, I've added my two classes.
ListTask Class
import 'package:flutter/material.dart';
class ListTask extends StatefulWidget {
const ListTask({Key? key}) : super(key: key);
#override
State<ListTask> createState() => _ListTaskState();
}
class _ListTaskState extends State<ListTask> {
final List<String> tasks = ['masu', 'adasf', 'wfqf', 'santha'];
final TextEditingController _textFieldController = TextEditingController();
void addItemToList() {
setState(() {
tasks.insert(0, _textFieldController.text);
});
}
#override
Widget build(BuildContext context) {
return Container(
height: 320,
width: double.maxFinite,
child: ListView.builder(
padding: EdgeInsets.only(bottom: 10),
itemCount: tasks.length,
itemBuilder: (context, index) {
return Card(
elevation: 1,
color: Colors.grey[200],
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ExpansionTile(title: Text(tasks[index]), children: <Widget>[
ListTile(
title: Text(tasks[index]),
)
]),
],
),
);
},
),
);
}
Future<void> _displayTextInputDialog(BuildContext context) async {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('TextField in Dialog'),
content: TextField(
onChanged: (value) {
setState(() {
// valueText = value;
});
},
controller: _textFieldController,
decoration: InputDecoration(hintText: "Text Field in Dialog"),
),
actions: <Widget>[
FlatButton(
color: Colors.red,
textColor: Colors.white,
child: Text('CANCEL'),
onPressed: () {
setState(() {
Navigator.pop(context);
});
},
),
FlatButton(
color: Colors.green,
textColor: Colors.white,
child: Text('OK'),
onPressed: () {
setState(() {
addItemToList();
Navigator.pop(context);
});
},
),
],
);
});
}
}
TaskApp Class
import 'package:flutter/material.dart';
import 'package:task_app/Widgets/listtasks.dart';
import 'package:task_app/Widgets/logo.dart';
import 'package:task_app/Widgets/searchbar.dart';
class TaskApp extends StatefulWidget {
const TaskApp({Key? key}) : super(key: key);
#override
State<TaskApp> createState() => _TaskAppState();
}
class _TaskAppState extends State<TaskApp> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 10),
Logo(),
SizedBox(height: 0),
SearchBar(),
SizedBox(height: 15),
Column(
children: [
Text(
'All tasks',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15.0),
)
],
),
SizedBox(height: 15),
ListTask(),
],
),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
_displayTextInputDialog(context);
},
label: const Text('Add Task'),
icon: const Icon(Icons.add),
backgroundColor: Colors.blue[900],
),
);
}
}
Calling point of that method in TaskApp Class:
Method:
You can give this a try, it will call a method defined in ListTask(StatefulWidget) from TaskApp(StatefulWidget) widget.
TaskApp.dart
import 'package:flutter/material.dart';
import 'package:vcare/Screens/tetxing1.dart';
class TaskApp extends StatefulWidget {
final ListTask listTask;
const TaskApp({Key? key,required this.listTask}) : super(key: key);
#override
State<TaskApp> createState() => _TaskAppState();
}
class _TaskAppState extends State<TaskApp> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 10),
// Logo(),
SizedBox(height: 0),
//SearchBar(),
SizedBox(height: 15),
Column(
children: [
Text(
'All tasks',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15.0),
)
],
),
SizedBox(height: 15),
ListTask(),
],
),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
ListTask().method1(context);
},
label: const Text('Add Task'),
icon: const Icon(Icons.add),
backgroundColor: Colors.blue[900],
),
);
}
}
ListTask.dart
import 'package:flutter/material.dart';
class ListTask extends StatefulWidget {
method1(context) => createState().displayTextInputDialog(context);
#override
_ListTaskState createState() => _ListTaskState();
}
class _ListTaskState extends State<ListTask> {
final List<String> tasks = ['masu', 'adasf', 'wfqf', 'santha'];
final TextEditingController _textFieldController = TextEditingController();
void addItemToList() {
setState(() {
tasks.insert(0, _textFieldController.text);
});
}
#override
Widget build(BuildContext context) {
return Container(
height: 320,
width: double.maxFinite,
child: ListView.builder(
padding: EdgeInsets.only(bottom: 10),
itemCount: tasks.length,
itemBuilder: (context, index) {
return Card(
elevation: 1,
color: Colors.grey[200],
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ExpansionTile(title: Text(tasks[index]), children: <Widget>[
ListTile(
title: Text(tasks[index]),
)
]),
],
),
);
},
),
);
}
Future<void> displayTextInputDialog(BuildContext context) async {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('TextField in Dialog'),
content: TextField(
onChanged: (value) {
setState(() {
// valueText = value;
});
},
controller: _textFieldController,
decoration: InputDecoration(hintText: "Text Field in Dialog"),
),
actions: <Widget>[
FlatButton(
color: Colors.red,
textColor: Colors.white,
child: Text('CANCEL'),
onPressed: () {
setState(() {
Navigator.pop(context);
});
},
),
FlatButton(
color: Colors.green,
textColor: Colors.white,
child: Text('OK'),
onPressed: () {
setState(() {
addItemToList();
Navigator.pop(context);
});
},
),
],
);
});
}
}
if you find this solution helpful please mark as accepted answer

Failed Assertion: boolean expression must not be null. Initially the loading screen is called

My code is as follows:
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<CategoryModel> categories = new List<CategoryModel>();
List<ArticleModel> articles = new List<ArticleModel>();
bool _loading = true;
getNews() async{
News newsClass = News();
await newsClass.getNews();
articles = newsClass.news;
setState(() {
_loading = false;
});
}
#override
void initState() {
getNews();
categories = getCategories();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("News"),
Text("App", style: TextStyle(
color: Colors.blueAccent
),)
],
),
centerTitle: true,
elevation: 0.0,
),
body: _loading ? Center(
child: CircularProgressIndicator(),
) : SingleChildScrollView(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: <Widget>[
/// Categories
Container(
height: 70,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: categories.length,
itemBuilder: (context, index){
return CategoryTile(
imageUrl: categories[index].imageUrl,
categoryName: categories[index].categoryName,
);
}),
),
/// Blog
Container(
padding: EdgeInsets.only(top: 16),
child: ListView.builder(
itemCount: articles.length,
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemBuilder: (context, index){
return BlogTile(
imageUrl: articles[index].urlToImage,
title: articles[index].title,
desc: articles[index].description,
);
}),
)
],
),
),
),
);
}
}
class CategoryTile extends StatelessWidget {
final imageUrl, categoryName;
CategoryTile({this.imageUrl, this.categoryName});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: (){
},
child: Container(
margin: EdgeInsets.only(right: 16),
child: Stack(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.circular(6),
child: CachedNetworkImage(
imageUrl: imageUrl, width: 120, height: 60, fit: BoxFit.cover,)
),
Container(
alignment: Alignment.center,
width: 120, height: 60,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: Colors.black26,
),
child: Text(categoryName, style: TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w500
),),
)
],
),
),
);
}
}
class BlogTile extends StatelessWidget {
final String imageUrl, title, desc;
BlogTile({#required this.imageUrl,#required this.title,#required this.desc});
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.circular(6),
child: Image.network(imageUrl)
),
SizedBox(height: 8,),
Text(title, style: TextStyle(
fontSize: 17, color: Colors.black87, fontWeight: FontWeight.w500
),),
SizedBox(height: 8,),
Text(desc, style: TextStyle(
color: Colors.black54
),)
],
),
);
}
}
When I run the code it shows the loading screen which is not going off, the content which i want to display is not showing. Please help by providing your valuable answer. Thank you in advance.
The error I am getting is as follows:
E/flutter ( 5339): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: Failed assertion: boolean expression must not be null
Since you are initialising your _loading variable inside the first statement of the initState. You can as well initialise it directly when declaring it.
Like this:
bool _loading = true;
Try the code below:
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<CategoryModel> categories = new List<CategoryModel>();
List<ArticleModel> articles = new List<ArticleModel>();
bool _loading = true;
getNews() async{
News newsClass = News();
await newsClass.getNews();
articles = newsClass.news;
}
#override
void initState() {
getNews();
categories = getCategories();
setState(() {
_loading = false;
});
super.initState();
}
I hope this helps.

MyAppState is not displaying anything

import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:image_picker/image_picker.dart';
import 'package:qrscan/qrscan.dart' as scanner;
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Uint8List bytes = Uint8List(0);
TextEditingController _inputController;
TextEditingController _outputController;
#override
initState() {
super.initState();
this._inputController = new TextEditingController();
this._outputController = new TextEditingController();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Colors.grey[300],
body: Builder(
builder: (BuildContext context) {
return ListView(
children: <Widget>[
_qrCodeWidget(this.bytes, context),
Container(
color: Colors.white,
child: Column(
children: <Widget>[
TextField(
controller: this._inputController,
keyboardType: TextInputType.url,
textInputAction: TextInputAction.go,
onSubmitted: (value) => _generateBarCode(value),
decoration: InputDecoration(
prefixIcon: Icon(Icons.text_fields),
helperText: 'Please input your code to generage qrcode image.',
hintText: 'Please Input Your Code',
hintStyle: TextStyle(fontSize: 15),
contentPadding: EdgeInsets.symmetric(
horizontal: 7, vertical: 15),
),
),
SizedBox(height: 20),
TextField(
controller: this._outputController,
maxLines: 2,
decoration: InputDecoration(
prefixIcon: Icon(Icons.wrap_text),
helperText: 'The barcode or qrcode you scan will be displayed in this area.',
hintText: 'The barcode or qrcode you scan will be displayed in this area.',
hintStyle: TextStyle(fontSize: 15),
contentPadding: EdgeInsets.symmetric(
horizontal: 7, vertical: 15),
),
),
SizedBox(height: 20),
this._buttonGroup(),
SizedBox(height: 70),
],
),
),
],
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => _scanBytes(),
tooltip: 'Take a Photo',
child: const Icon(Icons.camera_alt),
),
),
);
}
Widget _qrCodeWidget(Uint8List bytes, BuildContext context) {
return Padding(
padding: EdgeInsets.all(20),
child: Card(
elevation: 6,
child: Column(
children: <Widget>[
Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Icon(Icons.verified_user, size: 18, color: Colors.green),
Text(' Generate Qrcode', style: TextStyle(fontSize: 15)),
Spacer(),
Icon(Icons.more_vert, size: 18, color: Colors.black54),
],
),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 9),
decoration: BoxDecoration(
color: Colors.black12,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(4), topRight: Radius.circular(4)),
),
),
Padding(
padding: EdgeInsets.only(
left: 40, right: 40, top: 30, bottom: 10),
child: Column(
children: <Widget>[
SizedBox(
height: 190,
child: bytes.isEmpty
? Center(
child: Text('Empty code ... ',
style: TextStyle(color: Colors.black38)),
)
: Image.memory(bytes),
),
Padding(
padding: EdgeInsets.only(top: 7, left: 25, right: 25),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Expanded(
flex: 5,
child: GestureDetector(
child: Text(
'remove',
style: TextStyle(
fontSize: 15, color: Colors.blue),
textAlign: TextAlign.left,
),
onTap: () =>
this.setState(() => this.bytes = Uint8List(0)),
),
),
Text('|', style: TextStyle(fontSize: 15, color: Colors
.black26)),
Expanded(
flex: 5,
child: GestureDetector(
onTap: () async {
final success = await ImageGallerySaver.saveImage(
this.bytes);
SnackBar snackBar;
if (success) {
snackBar = new SnackBar(content: new Text(
'Successful Preservation!'));
Scaffold.of(context).showSnackBar(snackBar);
} else {
snackBar =
new SnackBar(content: new Text('Save failed!'));
}
},
child: Text(
'save',
style: TextStyle(
fontSize: 15, color: Colors.blue),
textAlign: TextAlign.right,
),
),
),
],
),
)
],
),
),
Divider(height: 2, color: Colors.black26),
],
),
),
);
}
Widget _buttonGroup() {
return Row(
children: <Widget>[
Expanded(
flex: 1,
child: SizedBox(
height: 120,
child: InkWell(
onTap: () => _generateBarCode(this._inputController.text),
child: Card(
child: Column(
children: <Widget>[
Expanded(
flex: 2,
child: Image.asset('images/generate_qrcode.png'),
),
Divider(height: 20),
Expanded(flex: 1, child: Text("Generate")),
],
),
),
),
),
),
Expanded(
flex: 1,
child: SizedBox(
height: 120,
child: InkWell(
onTap: _scan,
child: Card(
child: Column(
children: <Widget>[
Expanded(
flex: 2,
child: Image.asset('images/scanner.png'),
),
Divider(height: 20),
Expanded(flex: 1, child: Text("Scan")),
],
),
),
),
),
),
Expanded(
flex: 1,
child: SizedBox(
height: 120,
child: InkWell(
onTap: _scanPhoto,
child: Card(
child: Column(
children: <Widget>[
Expanded(
flex: 2,
child: Image.asset('images/albums.png'),
),
Divider(height: 20),
Expanded(flex: 1, child: Text("Scan Photo")),
],
),
),
),
),
),
],
);
}
Future _scan() async {
String barcode = await scanner.scan();
if (barcode == null) {
print('nothing return.');
} else {
this._outputController.text = barcode;
}
}
Future _scanPhoto() async {
String barcode = await scanner.scanPhoto();
this._outputController.text = barcode;
}
Future _scanPath(String path) async {
String barcode = await scanner.scanPath(path);
this._outputController.text = barcode;
}
Future _scanBytes() async {
File file = await ImagePicker.pickImage(source: ImageSource.camera);
Uint8List bytes = file.readAsBytesSync();
String barcode = await scanner.scanBytes(bytes);
this._outputController.text = barcode;
}
Future _generateBarCode(String inputCode) async {
Uint8List result = await scanner.generateBarCode(inputCode);
this.setState(() => this.bytes = result);
}
}
class MyAppState extends State<MyApp> {
Future<SharedPreferences> _sPrefs = SharedPreferences.getInstance();
final TextEditingController controller = TextEditingController();
List<String> listOne, listTwo;
#override
void initState() {
super.initState();
listOne = [];
listTwo = [];
}
Future<Null> addString() async {
final SharedPreferences prefs = await _sPrefs;
listOne.add(controller.text);
prefs.setStringList('list', listOne);
setState(() {
controller.text = '';
});
}
Future<Null> clearItems() async {
final SharedPreferences prefs = await _sPrefs;
prefs.clear();
setState(() {
listOne = [];
listTwo = [];
});
}
Future<Null> getStrings() async {
final SharedPreferences prefs = await _sPrefs;
listTwo = prefs.getStringList('list');
setState(() {});
}
Future<Null> updateStrings(String str) async {
final SharedPreferences prefs = await _sPrefs;
setState(() {
listOne.remove(str);
listTwo.remove(str);
});
prefs.setStringList('list', listOne);
}
#override
Widget build(BuildContext context) {
getStrings();
return Center(
child: ListView(
children: <Widget>[
TextField(
controller: controller,
decoration: InputDecoration(
hintText: 'Type in something...',
)),
RaisedButton(
child: Text("Submit"),
onPressed: () {
addString();
},
),
RaisedButton(
child: Text("Clear"),
onPressed: () {
clearItems();
},
),
Flex(
direction: Axis.vertical,
children: listTwo == null
? []
: listTwo
.map((String s) => Dismissible(
key: Key(s),
onDismissed: (direction) {
updateStrings(s);
},
child: ListTile(
title: Text(s),
)))
.toList(),
)
],
),
);
}
}
Here the first Appstate creates a Qr code reader. Second is for creating an input controller with shared preferences that can store and retrieve data locally. But when running the code the app displays only the qrscan part and 2nd is not working. I'm new to Flutter. I've just started working on Android Studio. Can anybody help please?
Try to call Another class from home property of the MyApp class.
As you can see in this image
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
//Here I have called MyWidget Class
home: MyWidget()
);
}
}
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
Uint8List bytes = Uint8List(0);
TextEditingController _inputController;
TextEditingController _outputController;
#override
initState() {
super.initState();
this._inputController = new TextEditingController();
this._outputController = new TextEditingController();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(

Flutter : How to prevent rebuild of whole Reorderable Listview?

Currently I'm using flutter package 'Reorderables' to show a reorderable listview which contains several images.These images can be deleted from listview through a button , everything works fine. But the listview rebuild everytime when I delete an image. I'm using a class called 'ReorderableListviewManager' with ChangeNotifier to update images and Provider.of<ReorderableListviewManager>(context) to get latest images . The problem now is that using Provider.of<ReorderableListviewManager>(context) makes build() called everytime I delete an image , so the listview rebuid. I koow I
can use consumer to only rebuild part of widget tree, but it seems like that there's no place to put consumer in children of this Listview. Is there a way to rebuild only image but not whole ReorderableListview ? Thanks very much!
Below is my code:
class NotePicturesEditScreen extends StatefulWidget {
final List<Page> notePictures;
final NotePicturesEditBloc bloc;
NotePicturesEditScreen({#required this.notePictures, #required this.bloc});
static Widget create(BuildContext context, List<Page> notePictures) {
return Provider<NotePicturesEditBloc>(
create: (context) => NotePicturesEditBloc(),
child: Consumer<NotePicturesEditBloc>(
builder: (context, bloc, _) =>
ChangeNotifierProvider<ReorderableListviewManager>(
create: (context) => ReorderableListviewManager(),
child: NotePicturesEditScreen(
bloc: bloc,
notePictures: notePictures,
),
)),
dispose: (context, bloc) => bloc.dispose(),
);
}
#override
_NotePicturesEditScreenState createState() => _NotePicturesEditScreenState();
}
class _NotePicturesEditScreenState extends State<NotePicturesEditScreen> {
PreloadPageController _pageController;
ScrollController _reorderableScrollController;
List<Page> notePicturesCopy;
int longPressIndex;
List<double> smallImagesWidth;
double scrollOffset = 0;
_reorderableScrollListener() {
scrollOffset = _reorderableScrollController.offset;
}
#override
void initState() {
Provider.of<ReorderableListviewManager>(context, listen: false)
.notePictures = widget.notePictures;
notePicturesCopy = widget.notePictures;
_reorderableScrollController = ScrollController();
_pageController = PreloadPageController();
_reorderableScrollController.addListener(_reorderableScrollListener);
Provider.of<ReorderableListviewManager>(context, listen: false)
.getSmallImagesWidth(notePicturesCopy, context)
.then((imagesWidth) {
smallImagesWidth = imagesWidth;
});
super.initState();
}
#override
void dispose() {
_pageController.dispose();
_reorderableScrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
ReorderableListviewManager reorderableManager =
Provider.of<ReorderableListviewManager>(context, listen: false);
return SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
shape: Border(bottom: BorderSide(color: Colors.black12)),
iconTheme: IconThemeData(color: Colors.black87),
elevation: 0,
automaticallyImplyLeading: false,
titleSpacing: 0,
centerTitle: true,
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: IconButton(
padding: EdgeInsets.only(left: 20, right: 12),
onPressed: () => Navigator.of(context).pop(),
icon: Icon(Icons.close),
),
),
Text('編輯',
style: TextStyle(color: Colors.black87, fontSize: 18))
],
),
actions: <Widget>[
FlatButton(
onPressed: () {},
child: Text(
'下一步',
),
)
],
),
backgroundColor: Color(0xffeeeeee),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Spacer(),
StreamBuilder<List<Page>>(
initialData: widget.notePictures,
stream: widget.bloc.notePicturesStream,
builder: (context, snapshot) {
notePicturesCopy = snapshot.data;
return Container(
margin: EdgeInsets.symmetric(horizontal: 20),
height: MediaQuery.of(context).size.height * 0.65,
child: PreloadPageView.builder(
preloadPagesCount: snapshot.data.length,
controller: _pageController,
itemCount: snapshot.data.length,
onPageChanged: (index) {
reorderableManager.updateCurrentIndex(index);
reorderableManager.scrollToCenter(
smallImagesWidth,
index,
scrollOffset,
_reorderableScrollController,
context);
},
itemBuilder: (context, index) {
return Container(
child: Image.memory(
File.fromUri(
snapshot.data[index].polygon.isNotEmpty
? snapshot.data[index]
.documentPreviewImageFileUri
: snapshot.data[index]
.originalPreviewImageFileUri)
.readAsBytesSync(),
gaplessPlayback: true,
alignment: Alignment.center,
),
);
}),
);
},
),
Spacer(),
Container(
height: MediaQuery.of(context).size.height * 0.1,
margin: EdgeInsets.symmetric(horizontal: 20),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ReorderableRow(
scrollController: _reorderableScrollController,
buildDraggableFeedback: (context, constraints, __) =>
Container(
width: constraints.maxWidth,
height: constraints.maxHeight,
child: Image.memory(File.fromUri(
notePicturesCopy[longPressIndex]
.polygon
.isNotEmpty
? notePicturesCopy[longPressIndex]
.documentPreviewImageFileUri
: notePicturesCopy[longPressIndex]
.originalPreviewImageFileUri)
.readAsBytesSync()),
),
onReorder: (oldIndex, newIndex) async {
List<Page> result = await widget.bloc.reorderPictures(
oldIndex,
newIndex,
reorderableManager.notePictures);
_pageController.jumpToPage(newIndex);
reorderableManager.updateNotePictures(result);
reorderableManager
.getSmallImagesWidth(result, context)
.then((imagesWidth) {
smallImagesWidth = imagesWidth;
});
},
footer: Container(
width: 32,
height: 32,
margin: EdgeInsets.only(left: 16),
child: SizedBox(
child: FloatingActionButton(
backgroundColor: Colors.white,
elevation: 1,
disabledElevation: 0,
highlightElevation: 1,
child: Icon(Icons.add, color: Colors.blueAccent),
onPressed: notePicturesCopy.length >= 20
? () {
Scaffold.of(context)
.showSnackBar(SnackBar(
content: Text('筆記上限為20頁 !'),
));
}
: () async {
List<Page> notePictures =
await widget.bloc.addPicture(
reorderableManager.notePictures);
List<double> imagesWidth =
await reorderableManager
.getSmallImagesWidth(
notePictures, context);
smallImagesWidth = imagesWidth;
reorderableManager.updateCurrentIndex(
notePictures.length - 1);
reorderableManager
.updateNotePictures(notePictures);
_pageController
.jumpToPage(notePictures.length - 1);
},
),
),
),
children: Provider.of<ReorderableListviewManager>(
context)
.notePictures
.asMap()
.map((index, page) {
return MapEntry(
index,
Consumer<ReorderableListviewManager>(
key: ValueKey('value$index'),
builder: (context, manager, _) =>
GestureDetector(
onTapDown: (_) {
longPressIndex = index;
},
onTap: () {
reorderableManager.scrollToCenter(
smallImagesWidth,
index,
scrollOffset,
_reorderableScrollController,
context);
_pageController.jumpToPage(index);
},
child: Container(
margin: EdgeInsets.only(
left: index == 0 ? 0 : 12),
decoration: BoxDecoration(
border: Border.all(
width: 1.5,
color: index ==
manager
.getCurrentIndex
? Colors.blueAccent
: Colors.transparent)),
child: index + 1 <=
manager.notePictures.length
? Image.memory(
File.fromUri(manager
.notePictures[
index]
.polygon
.isNotEmpty
? manager
.notePictures[
index]
.documentPreviewImageFileUri
: manager
.notePictures[
index]
.originalPreviewImageFileUri)
.readAsBytesSync(),
gaplessPlayback: true,
)
: null),
),
));
})
.values
.toList()),
)),
Spacer(),
Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border(top: BorderSide(color: Colors.black12))),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
FlatButton(
onPressed: () async => await widget.bloc
.cropNotePicture(reorderableManager.notePictures,
_pageController.page.round())
.then((notePictures) {
reorderableManager.updateNotePictures(notePictures);
reorderableManager
.getSmallImagesWidth(notePictures, context)
.then((imagesWidth) {
smallImagesWidth = imagesWidth;
});
}),
child: Column(
children: <Widget>[
Icon(
Icons.crop,
color: Colors.blueAccent,
),
Container(
margin: EdgeInsets.only(top: 1),
child: Text(
'裁切',
style: TextStyle(color: Colors.blueAccent),
),
)
],
),
),
FlatButton(
onPressed: () {
int deleteIndex = _pageController.page.round();
widget.bloc
.deletePicture(
reorderableManager.notePictures, deleteIndex)
.then((notePictures) {
if (deleteIndex == notePictures.length) {
reorderableManager
.updateCurrentIndex(notePictures.length - 1);
}
reorderableManager.updateNotePictures(notePictures);
reorderableManager
.getSmallImagesWidth(notePictures, context)
.then((imagesWidth) {
smallImagesWidth = imagesWidth;
});
if (reorderableManager.notePictures.length == 0) {
Navigator.pop(context);
}
});
},
child: Column(
children: <Widget>[
Icon(
Icons.delete_outline,
color: Colors.blueAccent,
),
Container(
margin: EdgeInsets.only(top: 1),
child: Text(
'刪除',
style: TextStyle(color: Colors.blueAccent),
),
),
],
),
)
],
),
)
],
)),
);
}
}
You can't prevent a rebuild on your ReorderableListView widget because it will be rebuild every time there's an update on the Provider. What you can do here is to keep track the current index of all visible ListView items. When new data should be displayed coming from the Provider, you can retain the current indices of previous ListView items, and add the newly added items at the end of the list, or wherever you like.

Categories

Resources