How do I add loading screen in Flutter - flutter

I am making an app with flutter and I want a loading screen while fetching data from firestore I used to do this in android by setvisibilty .I am new to flutter and I don't know how to do it I saw some questions on stack but they didn't seem to help full
I want to show the loading screen if firebaseUser is not null,
this is my initState method
void initState() {
super.initState();
isRegistered();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.all(32),
child: Form(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Login"),
SizedBox(
height: 16,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Container(
width: 50,
child: TextFormField(
maxLength: 4,
keyboardType: TextInputType.number,
controller: countryCodeController,
decoration: InputDecoration(
hintText: '+251',
),
),
),
Container(
width: 200,
child: TextFormField(
maxLength: 9,
keyboardType: TextInputType.number,
controller: phonenumberController,
decoration: InputDecoration(
hintText: '912345678',
),
),
),
],
),
SizedBox(
height: 16,
),
Container(
width: double.infinity,
child: FlatButton(
child: Text('Login'),
color: Colors.white,
padding: EdgeInsets.all(16),
onPressed: () {
final phoneNumber = countryCodeController.text.trim() + phonenumberController.text.trim();
if(phonenumberController.text.trim().length == 9 || countryCodeController.text.trim().length == 4){
loginUser(phoneNumber, context);
}else{
Fluttertoast.showToast(msg: "wronge input");
}
}),
)
],
),
),
),
);
}
void isRegistered() async{
FirebaseAuth firebaseAuth = FirebaseAuth.instance;
final FirebaseUser firebaseUser = await firebaseAuth.currentUser();
final snapShot = await Firestore.instance.collection("users").document(
firebaseUser.uid).get();
if (firebaseUser != null) {
if (snapShot.exists) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => HomePage(
firebaseUser: firebaseUser,
)));
}else{
}
}
}
}

Just check out this example I have created for you:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _isLoading = false; // This is initially false where no loading state
List<Timings> timingsList = List();
#override
void initState() {
super.initState();
dataLoadFunction(); // this function gets called
}
dataLoadFunction() async {
setState(() {
_isLoading = true; // your loader has started to load
});
// fetch you data over here
setState(() {
_isLoading = false; // your loder will stop to finish after the data fetch
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: _isLoading
? CircularProgressIndicator() // this will show when loading is true
: Text('You widget tree after loading ...') // this will show when loading is false
),
);
}
}
Let me know if it works

I use flutter_spinkit for the animation.
The package flutter_spinkit is a collection of loading indicators animated with flutter.
Here the Loading widget:
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
class Loading extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Center(
child: SpinKitFadingCube(
color: Colors.lightGreen[100],
size: 50.0
)
)
);
}
}
Then, from within your widgets, you need to:
import '[yourpath]/loading.dart';
bool loading = false;
#override
Widget build(BuildContext context) {
return loading ? Loading() : Scaffold(
body: Container(...
Wherever is your click event, you should set the state of loading to TRUE:
setState(() => loading = true)
and where the callback is, you should set the state back to FALSE:
setState(() => loading = false)

You can try creating a widget component such as this and save it with the name progress.dart
import 'package:flutter/material.dart';
Container circularProgress() {
return Container(
alignment: Alignment.center,
padding: EdgeInsets.only(top: 10.0),
child: CircularProgressIndicator(
strokeWidth: 2.0,
valueColor: AlwaysStoppedAnimation(primaryColor), //any color you want
),
);
}
Then import the progress.dart and create a separate container
Container loadingScreen() {
return circularProgress();
}
Then change your code to:
class RootScreenSM extends StatefulWidget {
#override
_RootScreenSMState createState() => _RootScreenSMState();
}
class _RootScreenSMState extends State<RootScreenSM> {
#override
Widget build(BuildContext context) {
return StreamBuilder<FirebaseUser>(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return loadingScreen(); // Container that you just created
} else {
if (snapshot.hasData) {
return HomePage(
firebaseUser: snapshot.data,
);
} else {
return
notloggedin();
}
}
},
);
}
You can try this method and let us know if it worked

You can do something like this
class RootScreenSM extends StatefulWidget {
#override
_RootScreenSMState createState() => _RootScreenSMState();
}
class _RootScreenSMState extends State<RootScreenSM> {
#override
Widget build(BuildContext context) {
return StreamBuilder<FirebaseUser>(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return new Container(
color: Colors.white,
//customize container look and feel here
);
} else {
if (snapshot.hasData) {
return HomePage(
firebaseUser: snapshot.data,
);
} else {
return
notloggedin();
}
}
},
);
}

Related

How to hide or show widget built inside FutureBuilder based on daynamic changes

Am able to show and hide the widget i want but it keeps flickering or rebuilding its self every time.
i just want to show the the capture icon button when the compass degree reaches 190 degree
this is my main widget
late List<CameraDescription> cameras;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
cameras = await availableCameras();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: CameraApp(),
);
}
}
class CameraApp extends StatefulWidget {
#override
_CameraAppState createState() => _CameraAppState();
}
class _CameraAppState extends State<CameraApp> {
String? imagePath;
XFile? imageFile;
late CameraController controller;
late Future<void> _initializeControllerFuture;
int? angleResult;
bool showCaptureButton = false;
String? getLocation;
bool k = false;
void getAngleFromCompass(newResult) {
WidgetsBinding.instance!.addPostFrameCallback((_) {
setState(() {
angleResult = newResult;
});
});
}
void getLocationRes(newResult) {
getLocation = newResult;
}
#override
void initState() {
super.initState();
controller = CameraController(
// Get a specific camera from the list of available cameras.
cameras[0],
// Define the resolution to use.
ResolutionPreset.high,
imageFormatGroup: ImageFormatGroup.yuv420,
);
_initializeControllerFuture = controller.initialize().then((value) {
setState(() {});
});
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
void _captureImage() async {
await takePicture().then((filePath) {
if (mounted) {
setState(() {
imagePath = filePath;
});
}
});
}
Widget cameraWidget(context) {
var camera = controller.value;
final size = MediaQuery.of(context).size;
var scale = size.aspectRatio * camera.aspectRatio;
if (scale < 1) scale = 1 / scale;
return Transform.scale(
scale: scale,
child: Center(
child: CameraPreview(controller),
),
);
}
#override
Widget build(BuildContext context) {
if (!controller.value.isInitialized) {
return Container();
}
theCompassApp(getAngleFromCompass) keeps flikering here
return Scaffold(
body: FutureBuilder(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Stack(
children: [
cameraWidget(context),
Align(
alignment: Alignment.topCenter,
child: LocationApp(getLocationRes),
),
Align(
child: CompassApp(getAngleFromCompass),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
color: Color(0xAA333639),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
angleResult == 190 ? IconButton(
onPressed: () => _captureImage(),
iconSize: 40,
icon: Icon(
Icons.camera_alt,
color: Colors.white,
),
) : Text(''),
],
),
),
)
],
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
Compass App Widget
class CompassApp extends StatefulWidget {
final getAngleValue;
CompassApp(this.getAngleValue);
#override
_CompassAppState createState() => _CompassAppState();
}
class _CompassAppState extends State<CompassApp> {
bool _hasPermissions = false;
#override
void initState() {
super.initState();
_fetchPermissionStatus();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Colors.transparent,
body: Builder(builder: (context) {
if (_hasPermissions) {
return Column(
children: <Widget>[
Expanded(child: _buildCompass()),
],
);
} else {
return Text('');
}
}),
),
);
}
Widget _buildCompass() {
return StreamBuilder<CompassEvent>(
stream: FlutterCompass.events,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 30),
child: Text('Error reading heading not support'),
);
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
double? direction = snapshot.data!.heading;
int ang = direction!.round();
Compass angle passed to main widget here
widget.getAngleValue(ang);
if (direction.isNaN)
return Center(
child: Text("Device does not have sensors !"),
);
return Material(
color: Colors.transparent,
child: Column(
children: [
RotatedBox(
quarterTurns: 1,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 41),
child: Text(
'$ang',
style: TextStyle(
fontSize: 50,
color: Colors.white,
backgroundColor: Colors.black26),
),
),
),
],
),
);
},
);
}
void _fetchPermissionStatus() {
Permission.locationWhenInUse.status.then((status) {
if (mounted) {
setState(() => _hasPermissions = status == PermissionStatus.granted);
}
});
}
}
Have you tried Visibility or Opacity?
Visibility
Visibility(
visible: true, //false for invisible
child: Text('I am visible'),
),
Opacity
Opacity(
opacity: 1.0, //0.0 for invisible
child: Text('I am visible'),
),

Preview photo after camera takes picture

When a user takes a photo, I want to send it to the photo_preview screen, which gives the user the chance to take another photo.
This page is as follows:
import 'package:flutter/material.dart';
import 'dart:io';
class photo_previewScreen extends StatelessWidget {
final String imagePath;
const photo_previewScreen({Key key, this.imagePath}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Display the Picture')),
body: Image.file(File(imagePath)),
);
}
}
On my current camera page, what's the best way for me to send the photo to the above page?
When the take picture button is pressed, this is what is currently happening:
onPressed: () {
_openGallery();
Navigator.pop(context);
},
EDIT: Full page with edits from the answer
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'dart:io';
import 'package:stumble/pages/PhotoPreviewScreen.dart';
class Camera extends StatefulWidget {
Function setData;
Camera({Key key, this.setData}) : super(key: key);
#override
_CameraScreenState createState() => _CameraScreenState();
}
class _CameraScreenState extends State<Camera> {
CameraController controller;
List cameras;
int selectedCameraIndex;
String imgPath;
var image;
takePicture;
Future _openGallery() async {
image = await controller.takePicture();
if (widget.setData != null) {
widget.setData(File(image.path));
}
}
#override
void initState() {
super.initState();
availableCameras().then((availableCameras) {
cameras = availableCameras;
if (cameras.length > 0) {
setState(() {
selectedCameraIndex = 0;
});
_initCameraController(cameras[selectedCameraIndex]).then((void v) {});
} else {
print('No camera available');
}
}).catchError((err) {
print('Error :${err.code}Error message : ${err.message}');
});
}
Future _initCameraController(CameraDescription cameraDescription) async {
if (controller != null) {
await controller.dispose();
}
controller = CameraController(cameraDescription, ResolutionPreset.high);
controller.addListener(() {
if (mounted) {
setState(() {});
}
if (controller.value.hasError) {
print('Camera error ${controller.value.errorDescription}');
}
});
try {
await controller.initialize();
} on CameraException catch (e) {}
if (mounted) {
setState(() {});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
flex: 1,
child: _cameraPreviewWidget(),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
height: 120,
width: double.infinity,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[_cameraControlWidget(context), Spacer()],
),
),
)
],
),
),
),
);
}
Widget _cameraPreviewWidget() {
if (controller == null || !controller.value.isInitialized) {
return const Text(
'Loading',
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.w900,
),
);
}
final size = MediaQuery.of(context).size;
final deviceRatio = size.width / size.height;
return Stack(children: <Widget>[
Positioned.fill(
child: new AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: new CameraPreview(controller),
),
),
]);
}
Widget _cameraControlWidget(context) {
return Expanded(
child: Align(
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
FloatingActionButton(
child: Icon(
Icons.center_focus_strong,
size: 39,
color: Color(0xffffffff),
),
backgroundColor: Color(0xff33333D),
onPressed: () async {
var result = await takePicture();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PhotoPreviewScreen(
imagePath: result,
),
),
);
})
],
),
),
);
}
}
I am getting the error that
'The method 'takePicture isn't defined for the type '_CameraScreenState'
This despite takePicture(); being defined.
You can navigate to the preview screen on clicking the take picture button, passing the image path:
void onTakePictureButtonPressed() async {
var result = await takePicture();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PhotoPreviewScreen(
imagePath: result,
),
),
);
}
Future<String> takePicture() async {
if (!controller.value.isInitialized) {
showErrorFlushbar(context, 'Error: select a camera first.');
return null;
}
final Directory extDir = await getApplicationDocumentsDirectory();
final String dirPath = '${extDir.path}/Pictures/ompariwar';
await Directory(dirPath).create(recursive: true);
final String filePath = '$dirPath/${timestamp()}.jpg';
if (controller.value.isTakingPicture) {
// A capture is already pending, do nothing.
return null;
}
try {
await controller.takePicture(filePath);
} on CameraException catch (e) {
print(e);
return null;
}
return filePath;
}
In the preview screen, you can do this:
class PhotoPreviewScreen extends StatelessWidget {
final String imagePath;
const PhotoPreviewScreen({Key key, this.imagePath}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.pop(context), // Go back to the camera to take the picture again
child: Icon(Icons.camera_alt),
),
appBar: AppBar(title: Text('Display the Picture')),
body: Column(
children: [
Expanded(child: Image.file(File(imagePath))),
SomeButton(), // Add a button to send the image to server or go back to home screen here
],
),
);
}
}
Btw it's a convention to name your class/ Widget name in UpperCamelCase. Read more on this Dart style guide for clean code.

how Can i make this Single selection Flutter?

I have an Apps which is having a listview with the reaction button in a flutter . I want to make this when a user clicked any of this love icon then it's filled with red color.
enter image description here
enter image description here
Like this image but the problem is when I clicked one of this love icon all of the icons turned into red color but I only want to change the color of love of icon which one is Selected.
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(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool like;
#override
List<String> user = ['Dipto', 'Dipankar', "Sajib", 'Shanto', 'Pranto'];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ListView Demu'),
),
body: Center(
child: Container(
child: ListView.builder(
itemCount: user.length,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(10),
height: 50,
width: MediaQuery.of(context).size.width * 0.8,
color: Colors.yellowAccent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
user[index],
),
Positioned(
child: IconButton(
icon: _iconControl(like),
onPressed: () {
if (like == false) {
setState(() {
like = true;
_iconControl(like);
});
} else {
setState(() {
like = false;
_iconControl(like);
});
}
},
),
),
],
),
);
},
),
)),
);
}
_iconControl(bool like) {
if (like == false) {
return Icon(Icons.favorite_border);
} else {
return Icon(
Icons.favorite,
color: Colors.red,
);
}
}
}
I also try with using parameter but Its failed Like that :
child: IconButton(
icon: _iconControl(true),
onPressed: () {
if (false) {
setState(() {
_iconControl(true);
});
} else {
setState(() {
_iconControl(false);
});
}
},
),
Can you help me Please. Thanks in advance
You can create a modal class to manage the selection of your list
Just create a modal class and add a boolean variable to maintaining selection using. that boolean variable
SAMPLE CODE
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(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool like;
List<Modal> userList = List<Modal>();
#override
void initState() {
userList.add(Modal(name: 'Dipto', isSelected: false));
userList.add(Modal(name: 'Dipankar', isSelected: false));
userList.add(Modal(name: 'Sajib', isSelected: false));
userList.add(Modal(name: 'Shanto', isSelected: false));
userList.add(Modal(name: 'Pranto', isSelected: false));
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ListView Demu'),
),
body: Center(
child: Container(
child: ListView.builder(
itemCount: userList.length,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(10),
height: 50,
width: MediaQuery
.of(context)
.size
.width * 0.8,
color: Colors.yellowAccent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
userList[index].name,
),
Positioned(
child: IconButton(
icon: _iconControl( userList[index].isSelected),
onPressed: () {
setState(() {
userList.forEach((element) {
element.isSelected = false;
});
userList[index].isSelected = true;
});
},
),
),
],
),
);
},
),
)),
);
}
_iconControl(bool like) {
if (like == false) {
return Icon(Icons.favorite_border);
} else {
return Icon(
Icons.favorite,
color: Colors.red,
);
}
}
}
class Modal {
String name;
bool isSelected;
Modal({this.name, this.isSelected = false});
}

Change the color of the container when clicked on it in my flutter app

I fetched some interest(data) through an API and show them using future builders as containers. I want to change the background color of the container when I clicked on it. Here is what I did and it's changing the background color of all the containers when I clicked on one.
I added an if condition to the color of the container to check whether it is clicked or not
color: isClicked? Colors.white : Color(0xFFFFEBE7),
and set the isClicked state to true when clicked.
bool isClicked = false;
FutureBuilder(
future: GetInterests.getInterests(),
builder: (context, snapshot) {
final datalist = snapshot.data;
if (snapshot.connectionState ==
ConnectionState.done) {
return Expanded(
child: SizedBox(
height: 35,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Wrap(
direction: Axis.vertical,
children: <Widget>[
GestureDetector(
onTap: (){
final inte_id = "${datalist[index]['_id']}";
log(inte_id);
setState(() {
isClicked = true;
});
},
child: new Container(
margin: EdgeInsets.only(right: 7),
height: 30,
width: MediaQuery.of(context)
.size
.width /
5.2,
decoration: BoxDecoration(
color: isClicked? Colors.white : Color(0xFFFFEBE7),
border: Border.all(
color: Color(0xFFE0E0E0)),
borderRadius:
BorderRadius.only(
topLeft:
Radius.circular(
50.0),
topRight:
Radius.circular(
50.0),
bottomRight:
Radius.circular(
50.0),
bottomLeft:
Radius.circular(
0.0))),
child: Center(
child: Text(
"${datalist[index]['iname']}",
style: TextStyle(
fontFamily: 'Montserrat',
color: Color(0xFFFF5E3A),
fontSize: 13),
),
),
),
),
],
);
},
itemCount: datalist.length,
),
),
);
}
return Padding(
padding: const EdgeInsets.only(left: 140.0),
child: Center(
child: CircularProgressIndicator(),
),
);
},
)
I was able to print the interest id in the console which belongs to the container I clicked on. but don't know how to change its color only
Instead of this you can use a variable to store selectedIndex and check if the currentIndex is selected or not and compare if currentIndex is selected or not and style the selected widget.
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
_MyWidgetState createState()=>_MyWidgetState();
}
class _MyWidgetState extends State<MyWidget>{
List _selectedIndexs=[];
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: 4,
itemBuilder: (ctx,i){
final _isSelected=_selectedIndexs.contains(i);
return GestureDetector(
onTap:(){
setState((){
if(_isSelected){
_selectedIndexs.remove(i);
}else{
_selectedIndexs.add(i);
}
});
},
child:Container(
color:_isSelected?Colors.red:null,
child:ListTile(title:Text("Khadga")),
),
);
}
);
}
}
modify your listview builder as i have done in above case.
While the accepted answer will work, a much more sophisticated architecture using ChangeNotifier and package provider will produce more loosely coupled, better code, in some folks opinion.
Combining ideas from the following
https://flutter.dev/docs/cookbook/networking/fetch-data
https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple
I was focused on architecture and data flow. Not on widget layout to match the original question's screenshot.
import 'dart:collection';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';
// Model ---------------------------------------------------
class Interest with ChangeNotifier {
final String title;
bool _selected = false;
Interest({
#required this.title,
}) : assert(title != null);
factory Interest.fromMap(final Map<String, dynamic> map) {
return Interest(
title: map['title'],
);
}
bool get selected {
return this._selected;
}
void select() {
this._selected = true;
this.notifyListeners();
}
void toggleSelect() {
this._selected = !this._selected;
this.notifyListeners();
}
}
class Interests extends ChangeNotifier {
final List<Interest> _interests = <Interest>[];
Interests();
factory Interests.fromList(final List<Map<String, dynamic>> list) {
final Interests interests = Interests();
for (final Map<String, dynamic> map in list) {
interests.add(Interest.fromMap(map));
}
return interests;
}
int get length {
return this._interests.length;
}
Interest operator [](final int index) {
return this._interests[index];
}
UnmodifiableListView<Interest> get interests {
return UnmodifiableListView<Interest>(this._interests);
}
void add(final Interest interest) {
this._interests.add(interest);
this.notifyListeners();
}
void selectAll() {
for (final Interest interest in this._interests) {
interest.select();
}
}
}
// Services ------------------------------------------------
Future<Interests> fetchInterests() async {
// Some data source that has a list of objects with titles.
final response = await http.get('https://jsonplaceholder.typicode.com/posts');
if (response.statusCode == 200) {
return Interests.fromList(json.decode(response.body).cast<Map<String, dynamic>>());
} else {
throw Exception('Failed to load post');
}
}
// User Interface ------------------------------------------
void main() {
runApp(InterestsApp());
}
class InterestsApp extends StatelessWidget {
#override
Widget build(final BuildContext context) {
return MaterialApp(
title: 'Interests App',
theme: ThemeData(primarySwatch: Colors.blue),
home: InterestsPage(),
);
}
}
class InterestsPage extends StatelessWidget {
#override
Widget build(final BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Interests')),
body: InterestsBody(),
);
}
}
class InterestsBody extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _InterestsBodyState();
}
}
class _InterestsBodyState extends State<InterestsBody> {
Future<Interests> _futureInterests;
#override
void initState() {
super.initState();
this._futureInterests = fetchInterests();
}
#override
Widget build(final BuildContext context) {
return FutureBuilder<Interests>(
future: this._futureInterests,
builder: (final BuildContext context, final AsyncSnapshot<Interests> snapshot) {
if (snapshot.hasData) {
return ChangeNotifierProvider.value(
value: snapshot.data,
child: InterestsList(),
);
} else if (snapshot.hasError) {
return Center(child: Text("${snapshot.error}"));
}
return Center(child: CircularProgressIndicator());
},
);
}
}
class InterestsList extends StatelessWidget {
#override
Widget build(final BuildContext context) {
return Consumer<Interests>(
builder: (final BuildContext context, final Interests interests, final Widget child) {
return Column(
children: <Widget>[
Center(
child: RaisedButton(
child: Text("Select All"),
onPressed: () {
interests.selectAll();
},
),
),
Expanded(
child: ListView.builder(
itemCount: interests.length,
itemBuilder: (final BuildContext context, final int index) {
return ChangeNotifierProvider<Interest>.value(
value: interests[index],
child: InterestTile(),
);
},
),
),
],
);
},
);
}
}
class InterestTile extends StatelessWidget {
#override
Widget build(final BuildContext context) {
return Consumer<Interest>(
builder: (final BuildContext context, final Interest interest, final Widget child) {
return ListTile(
title: Text(interest.title),
trailing: interest.selected ? Icon(Icons.check) : null,
onTap: () {
interest.toggleSelect();
},
);
},
);
}
}
you also can use flutter chip or ActionChip or ChoiceChip
class MyThreeOptions extends StatefulWidget {
#override
_MyThreeOptionsState createState() => _MyThreeOptionsState();
}
class _MyThreeOptionsState extends State<MyThreeOptions> {
int? _value = 1;
#override
Widget build(BuildContext context) {
return Wrap(
children: List<Widget>.generate(
3,
(int index) {
return ChoiceChip(
label: Text('Item $index'),
selected: _value == index,
onSelected: (bool selected) {
setState(() {
_value = selected ? index : null;
});
},
);
},
).toList(),
);
}
}

Flutter : problem re-rendering ListView using Stream Builder in bottom Navbar

I have a problem when displaying data to list view using the stream builder, my list view is always re-reload, when the tab is active.
i am implementing AutomaticKeepAliveClientMixin, but that is still not working.
here my code:
Home Bottom Nav :
https://pastebin.com/B9qf0zZR
List View index:
import 'package:eservice_f/src/blocs/listDataBloc.dart';
import 'package:eservice_f/src/models/listModel.dart';
import 'package:eservice_f/utils/layout.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class ServiceIndex extends StatefulWidget {
ServiceIndex({Key key}) : super(key: key);
_ServiceIndexState createState() => _ServiceIndexState();
}
class _ServiceIndexState extends State<ServiceIndex> with AutomaticKeepAliveClientMixin<ServiceIndex>{
ListDataBloc _bloc;
#override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
#override
void initState() {
// TODO: implement initState
super.initState();
_bloc = ListDataBloc();
_bloc.showAllData();
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
//bloclist.dispose();
}
#override
Widget build(BuildContext context) {
super.build(context);
return Container(
child: StreamBuilder(
stream: _bloc.allData,
builder: (context, AsyncSnapshot<ListData> snapshot) {
print(snapshot.data);
if (snapshot.hasData) {
return Container(
color: Colors.white,
child: Center(
child: getServiceList(context, snapshot),
),
);
} else if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
return Container(
color: Colors.white,
child: Center(child: CupertinoActivityIndicator()));
}),
);
}
Widget getServiceList(
BuildContext context, AsyncSnapshot<ListData> snapshot) {
SizeConfig().init(context);
var _list_data = snapshot.data.data;
return ListView.builder(
itemCount: _list_data.length,
itemBuilder: (BuildContext contex, int index) {
return Column(
children: <Widget>[
ListTile(
onTap: () {
print("List Tapped");
},
leading: Column(
children: <Widget>[
Icon(
Icons.check_circle_outline,
size: SizeConfig.blocHorizontal * 10,
),
],
),
title: Text("SBG/LK/20180814"),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('SusSystem Update Training MEX',
overflow: TextOverflow.ellipsis),
Text(
'So basically you building a Facebook/Instagram like application, where user logs in, scrolls through their feed, stalks through different profiles, and when done, wants to log out of the app',
overflow: TextOverflow.ellipsis),
],
),
trailing: (_list_data[index].activityStatus == "1")
? Text("Draft")
: Text("Confirm"),
),
Divider(
height: 1.0,
),
],
);
});
}
}
Bloc:
import 'package:eservice_f/src/models/listModel.dart';
import 'package:eservice_f/src/resources/repository.dart';
import 'package:rxdart/rxdart.dart';
class ListDataBloc{
final _repository = Repository();
final _fetcher = PublishSubject<ListData>();
Observable<ListData> get allData => _fetcher.stream;
showAllData() async{
ListData datas = await _repository.fetchAll();
_fetcher.sink.add(datas);
}
dispose(){
_fetcher.close();
}
}
//initial Bloc
//final bloclist = ListDataBloc();