I have web api that returns last image taken from database so I wish to make timer to show latest image every few seconds using Flutter and Dart. Ideally I would like to load image, shot it to the user and request new image from server infinitely. My issue is that before new image is displayed, black screen appears so it makes flickering effect. How can I solve it? Here is my code:
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class TestWidget extends StatefulWidget {
const TestWidget({Key? key}) : super(key: key);
#override
_CameraWidgetState createState() => _CameraWidgetState();
}
class _CameraWidgetState extends State<TestWidget> {
late int a = 1;
final scaffoldKey = GlobalKey<ScaffoldState>();
late bool loaded = false;
late Future futureRecords;
Future getImage() async {
final responses = await http.get(
Uri.parse("https://0465-95-178-160-106.ngrok.io/profile/download?picNum=" +
(a++).toString()),
headers: {
"Accept": "application/json",
"Access-Control_Allow_Origin": "*",
});
if (responses.statusCode == 200) {
return responses.bodyBytes;
} else {
throw Exception('Failed to load image');
}
}
#override
void initState() {
futureRecords = getImage();
Timer.periodic(Duration(seconds: 5), (timer) async {
try {
//await getImage();
setState(() {
futureRecords = getImage();
});
} catch (Ex) {}
});
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: futureRecords,
builder: (context, snapshot) {
if (!snapshot.hasData)
return Center(
child: CircularProgressIndicator(),
);
Uint8List bytes = snapshot.data as Uint8List;
return Image.memory(
bytes,
fit: BoxFit.fill,
);
});
}
}
Solution
You wrap your Testwidget with Material or Scaffold,Materialapp widget.You can AnimatedSwitcher widget to avoid that flickering effect or default color
Material(
child: AnimatedSwitcher(
duration: Duration(seconds: 1),
child: widget,
),
);
Before (Flickering)
class MYAppWithFlicker extends StatelessWidget {
const MYAppWithFlicker({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return TestWidget();
}
}
After (Without Flickering)
class MYAppWithoutFlicker extends StatelessWidget {
const MYAppWithoutFlicker({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Material(child: TestWidget());
}
}
Sample Code
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
void main() {
runApp(MYAppWithoutFlicker());
}
class MYAppWithoutFlicker extends StatelessWidget {
const MYAppWithoutFlicker({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Material(
color: Colors.lightGreen,
child: TestWidget(),
);
}
}
class MYAppWithFlicker extends StatelessWidget {
const MYAppWithFlicker({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return TestWidget();
}
}
class TestWidget extends StatefulWidget {
const TestWidget({Key? key}) : super(key: key);
#override
_CameraWidgetState createState() => _CameraWidgetState();
}
class _CameraWidgetState extends State<TestWidget> {
late int a = 1;
final scaffoldKey = GlobalKey<ScaffoldState>();
late bool loaded = false;
late Future futureRecords;
Future getImage() async {
var s = "https://0465-95-178-160-106.ngrok.io/profile/download?picNum=" +
(a++).toString();
final responses = await get(
Uri.parse(a % 2 == 0
? "https://images.indianexpress.com/2019/09/toys.jpg"
: "https://ecommerce.ccc2020.fr/wp-content/uploads/2020/10/electronic-gadgets.jpeg"),
headers: {
"Accept": "application/json",
"Access-Control_Allow_Origin": "*",
});
if (responses.statusCode == 200) {
print(responses.bodyBytes);
return responses.bodyBytes;
} else {
throw Exception('Failed to load image');
}
}
#override
void initState() {
futureRecords = getImage();
Timer.periodic(Duration(seconds: 5), (timer) async {
try {
//await getImage();
setState(() {
futureRecords = getImage();
});
} catch (Ex) {}
});
super.initState();
}
#override
Widget build(BuildContext context) {
var widget;
return FutureBuilder(
future: futureRecords,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
widget =
Center(child: CircularProgressIndicator());
else if (snapshot.connectionState == ConnectionState.done) {
Uint8List bytes = snapshot.data as Uint8List;
widget = Image.memory(
bytes,
fit: BoxFit.fill,
);
}
// return AnimatedSwitcher(
// duration: Duration(seconds: 1),
// child: widget,
// );
return Material(
child: AnimatedSwitcher(
duration: Duration(seconds: 1),
child: widget,
),
);
});
}
}
After few days of trying various methods, I found out about gaplessPlayback property in Image.memory and when I set it to true, all magically worked as expected.
Related
my question is can we use FutureBuilder with Void callbacks on using such methods whome type is
Future<void> fun_name() async{ body }
after creating such methods how can we pass that method to FutureBuilder widget on whatever thy type is/.
Yes
you did't mention what you are trying to accomplish and why you wnna do it but here is demonstration.
Here is demo widget
class Test extends StatelessWidget {
const Test({Key? key}) : super(key: key);
Future<void> fun(){
var s= Future.delayed(Duration(seconds: 10));
return s;
}
#override
Widget build(BuildContext context) {
return Container(
child: FutureBuilder<void>(
future: fun(),
builder: (snap,context){
return snap.widget; //return widget because builder's return type is widget
},
)
);
}
}
here i am doing it with async and await
class Test2 extends StatelessWidget {
const Test2({Key? key}) : super(key: key);
Future<void> delayedString() async {
await Future.delayed(const Duration(seconds: 2));
return;
}
#override
Widget build(BuildContext context) {
return Container(
child: FutureBuilder<void>(
future: delayedString(),
builder: (snap,context){
return snap.widget; //return widget because builder's return type is widget
},
)
);
}
}
How to remote end call or broadcast in flutter for agora video call like, for example:
import 'package:flutter/material.dart';
class VideoCalling extends StatelessWidget {
const VideoCalling({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
late RtcEngine _engine;
return Scaffold(
body: ElevatedButton(onPressed: (){
if(_remoteUser.isFirst) async {
await _engine.remoteDestroy();
await _engine.destroy();
await _engine.leave().then(value) => Navigator.pop(context);
} else{
await _engine.destroy();
await _engine.leave().then(value) => Navigator.pop(context);
}
}, child: Text("End all calls")),
);
}
}
Let's say I create a new screen team_screen which is the first parent of the tree.
Now for my team screen there are many widgets, some of theme have their own request, I want to show loader until every widget/request finished and ready.
I thought on 2 approaches.
All the requests are executed in team_screen with future builder and I pass the props to my widgets by demand.
Every widget with request get function that get executed in the async function in the initState function, then in my parent I make to every widget state parameter that is equal to true by the function I passed and when all is don't I stop the loader.
To sum up my problem is how to maintain a widget with many children and requests and showing one loader for entire page, making all the request on same widget? Pass isInitialize function to every widget?.
Which approach is better and if there are more approaches, I would like to hear.
Thank you for your help
Example for the second approach:
import 'package:flutter/material.dart';
import 'package:info_striker/locator.dart';
import 'package:info_striker/models/fixture/fixture.dart';
import 'package:info_striker/models/odds/bookmaker.dart';
import 'package:info_striker/models/synced-team/synced_team.dart';
import 'package:info_striker/services/fixture_service.dart';
import 'package:info_striker/utils/date_utilities.dart';
class TeamNextMatch extends StatefulWidget {
Function isInitialized;
SyncedTeam team;
TeamNextMatch({
Key? key,
required this.isInitialized,
required this.team,
}) : super(key: key);
#override
State<TeamNextMatch> createState() => _TeamNextMatchState();
}
class _TeamNextMatchState extends State<TeamNextMatch> {
Fixture? _fixture;
Bookmaker? _matchResult;
bool _isInitialized = false;
#override
void initState() {
super.initState();
init();
}
init() async {
final response = await locator<FixturesService>().getData(widget.team.id);
if (response != null) {
setState(() {
_fixture = Fixture.fromMap(response["fixture"]);
_matchResult = Bookmaker.fromMap(response["matchResultOdds"]);
});
}
widget.isInitialized(true);
}
#override
Widget build(BuildContext context) {
String? _date;
bool show = _fixture != null && _matchResult != null;
_fixture != null ? "${DateUtilities.getShortDateString(_fixture!.date)}, ${DateUtilities.getTimeString(_fixture!.date)}" : null;
return show
? Column(
children: [
Text(_fixture?.league?["name"]),
if (_date != null) Text(_date),
],
)
: const SizedBox();
}
}
You can show loader as described below -
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_application_1/data_model.dart';
import 'package:http/http.dart' as http;
class APiTest extends StatefulWidget {
const APiTest({Key? key}) : super(key: key);
#override
_APiTestState createState() => _APiTestState();
}
class _APiTestState extends State<APiTest> {
final String _url = "https://jsonplaceholder.typicode.com/todos/";
bool _isLoading = true;
final List<DataModel> _allData = [];
#override
void initState() {
super.initState();
_initData().then((value) {
setState(() {
_isLoading = false;
});
});
}
Future<void> _initData() async {
final response = await http.get(Uri.parse(_url));
final List res = jsonDecode(response.body);
res.forEach((element) {
_allData.add(DataModel.fromJson(element));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Loading Demo"),
),
body: Stack(
children: [
ListView.separated(
itemCount: _allData.length,
controller: ScrollController(),
separatorBuilder: (_, __) => const SizedBox(height: 10),
itemBuilder: ((context, index) {
return ListTile(
tileColor: Colors.grey[200],
title: Text(_allData[index].title!),
subtitle: Text(_allData[index].id.toString()),
);
}),
),
if (_isLoading)
const Center(
child: CircularProgressIndicator(),
)
],
),
);
}
}
this my code
Container(
child: Column(
children: jobProvider.jobs
.map(
(job) => JobTile(job),
)
.toList(),
),
)
how to load this data delay for 3 seconds ? here I display the listview data, before displaying it I want to display, the
return Container(
child: ProfileShimmer(),
);
Should be as easy as this:
import 'package:flutter/material.dart';
class PageWithDelayedView extends StatefulWidget {
const PageWithDelayedView({Key? key}) : super(key: key);
#override
_PageWithDelayedViewState createState() => _PageWithDelayedViewState();
}
class _PageWithDelayedViewState extends State<PageWithDelayedView> {
bool _initialized = false;
#override
void initState() {
super.initState();
// Schedule function call after the widget is ready to display
WidgetsBinding.instance?.addPostFrameCallback((_) {
_initialize();
});
}
void _initialize() {
Future<void>.delayed(const Duration(seconds: 3), () {
if (mounted) { // Check that the widget is still mounted
setState(() {
_initialized = true;
});
}
});
}
#override
Widget build(BuildContext context) {
if (!_initialized) {
return Text('Hold on a bit');
}
return Text('Yay, I\'m ready!');
}
}
I have question how to pass data between pages/screen in flutter without navigator and only using onChanged and streambuilder.
All I want is whenever user write in textfield on first widget, the second widget automatically refresh with the new data from first widget.
Here's my code for first.dart
import 'package:flutter/material.dart';
import 'second.dart';
class First extends StatefulWidget {
First({Key key}) : super(key: key);
#override
_FirstState createState() => _FirstState();
}
class _FirstState extends State<First> {
final TextEditingController _myTextController =
new TextEditingController(text: "");
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: Text("Passing Data"),
),
body: Column(
children: <Widget>[
Container(
height: 120.0,
child: Column(
children: <Widget>[
TextField(
controller: _myTextController,
onChanged: (String value) {
// refresh second with new data
},
)
]
)
),
Container(
height: 120.0,
child: Second(
myText: _myTextController.text,
),
)
],
),
);
}
}
and here's my second.dart as second widget to receive data from first widget.
import 'dart:async';
import 'package:flutter/material.dart';
import 'api_services.dart';
class Second extends StatefulWidget {
Second({Key key, #required this.myText}) : super(key: key);
final String myText;
#override
_SecondState createState() => _SecondState();
}
class _SecondState extends State<Second> {
StreamController _dataController;
loadPosts() async {
ApiServices.getDetailData(widget.myText).then((res) async {
_dataController.add(res);
return res;
});
}
#override
void initState() {
_dataController = new StreamController();
loadPosts();
super.initState();
print(widget.myText);
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: _dataController.stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError) {
return Text(snapshot.error);
}
if (snapshot.hasData) {
return Container();
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Row(
children: <Widget>[
Text("Please Write A Text"),
],
);
} else if (snapshot.connectionState != ConnectionState.active) {
return CircularProgressIndicator();
}
if (!snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return Text('No Data');
} else if(snapshot.hasData && snapshot.connectionState == ConnectionState.done) {
return Text(widget.myText);
}
return null;
});
}
}
You have a couple of options. The two simplest are - passing the text editing controller itself through to the second widget, then listening to it and calling setState to change the text in the second widget.
Example
class Second extends StatefulWidget {
Second({Key key, #required this.textController}) : super(key: key);
final TextEditingController textController;
#override
_SecondState createState() => _SecondState();
}
class _SecondState extends State<Second> {
// made this private
String _myText;
#override
void initState() {
_myText = widget.textController.text
widget.textController.addListener(() {
setState((){_myText = widget.textController.text});
);
});
super.initState();
}
...
// then in your build method, put this in place of return Text(widget.myText);
return Text(_myText);
Option 2 is listening to the controller in your first widget and call setState in there. This will rebuild both the first and second widget though, and I think is not as performant as the first option.
Hope that helps