In Flutter, can I put FutureBuilder inside a Timer? Is there a way to automatically refresh the GUI using FutureBuilder? Or any other possible way?
I am not sure what is your goal, but if you want to recreate your widget every X seconds with different strings you can use Future.delayed:
import 'package:flutter/material.dart';
import 'dart:async';
class RefreshWidget extends StatefulWidget {
RefreshWidget({Key? key, required this.title}) : super(key: key);
final String title;
#override
_RefreshWidgetState createState() => _RefreshWidgetState();
}
class _RefreshWidgetState extends State<RefreshWidget> {
static List<String> myStrings = ["aaa", "bbb", "ccc", "ddd"];
int i = 0;
String _current = myStrings[0];
void rebuild() {
Future.delayed(Duration(seconds: 3), () {
setState(() {
_current = myStrings[++i % 4];
});
rebuild();
});
}
#override
void initState() {
rebuild();
super.initState();
}
#override
Widget build(BuildContext context) {
return Material(child: Center(child: Text(_current)));
}
}
This package might help to refresh your widget periodically.
https://pub.dev/packages/refreshable_widget
Flexible(
child: RefreshableWidget<num>(
initialValue: challenge.userParticipation!.donePercent,
refreshCall: () async {
final challenge =
await cadoo.getChallengeDetail(
id: widget.challengeId,
);
return challenge.userParticipation!.donePercent;
},
builder: (context, value) {
return DonePercentWidget(
percent: value,
);
},
),
),
Related
GetX works fine and update data if element have controller. But how to get it work if we have not direct to controller and widget done in way of changing date with onChange.
I created small copy-paste example:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:web_date_picker/web_date_picker.dart';
void main() {
Get.put(SearchFormController());
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'GetX Demo',
home: MyHomePage(),
);
}
}
class SearchFormController extends GetxController {
var endDate = Rxn<DateTime?>();
setToNow() { // This function should set widget value
endDate.value = DateTime.now();
print('setToNow event: ${endDate.value}');
}
}
class MyHomePage extends StatelessWidget {
var ctrl = Get.find<SearchFormController>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Row(
children: [
Obx(() => SizedBox(
width: 160,
child: WebDatePicker(
initialDate: ctrl.endDate.value,
onChange: (value) {
if (value != null) {
ctrl.endDate.value = value;
print('onChange event: ${ctrl.endDate.value}');
}
},
),
)),
ElevatedButton(
onPressed: () {
ctrl.setToNow();
},
child: Text("Set Date"),
),
],
),
);
}
}
pubspec.yaml:
dependencies:
flutter:
sdk: flutter
web_date_picker: ^1.0.0+3
get: ^4.6.5
In this code I can't set date by clicking on Set Date button.
I looked at widget code and it's controller is hidden with follow realization:
class _WebDatePickerState extends State<WebDatePicker> {
final FocusNode _focusNode = FocusNode();
late OverlayEntry _overlayEntry;
final LayerLink _layerLink = LayerLink();
final _controller = TextEditingController();
late DateTime? _selectedDate;
late DateTime _firstDate;
late DateTime _lastDate;
bool _isEnterDateField = false;
#override
void initState() {
super.initState();
_selectedDate = widget.initialDate;
_firstDate = widget.firstDate ?? DateTime(2000);
_lastDate = widget.lastDate ?? DateTime(2100);
if (_selectedDate != null) {
_controller.text = _selectedDate?.parseToString(widget.dateformat) ?? '';
}
_focusNode.addListener(() {
if (_focusNode.hasFocus) {
_overlayEntry = _createOverlayEntry();
Overlay.of(context)?.insert(_overlayEntry);
} else {
_controller.text = _selectedDate.parseToString(widget.dateformat);
widget.onChange.call(_selectedDate);
_overlayEntry.remove();
}
});
}
void onChange(DateTime? selectedDate) {
_selectedDate = selectedDate;
_controller.text = _selectedDate.parseToString(widget.dateformat);
_focusNode.unfocus();
}
...
Widget code https://github.com/duchdtran/web_date_picker/blob/master/lib/src/web_date_picker.dart#L98
Could anybody provide example how to get button work to set widget value?
I experienced similar problems and solved them desribed as below.
It seems WebDatePicker does not process the value change. Try putting it in its own StatelessWidget:
class MyWebDatePicker extends StatelessClass {
final DateTime dt;
var ctrl = Get.find<SearchFormController>();
MyWebDatePicker(this.dt);
Widget build(BuildContext context) {
return WebDatePicker(
initialDate: dt,
onChange: (value) {
if (value != null) {
ctrl.endDate.value = value;
print('onChange event: ${value}');
}
},
);
}
}
Then, call it like given below. This code looks unusual, but it forces Obx to call MyWebDatePicker with the current DateTime value:
Obx(() => SizedBox(
width: 160,
child: MyWebDatePicker(ctrl.endDate.value)
)
)
i am clearing a hive box and updating it in the very next line.
The update can be seen in the same file code (through debugging).
But somehow for the other stateless widget/class/file box is empty.
boxT.clear() ;
setState(() {
boxT.addAll({
[count2, totalEntries]
});});
Reason for clearing the box on everytime a specific button is pressed: I am adding a map. addAll() simply creates another entry. i dont want it. i have also tried put and putall but they are only showing the value and not the key.
That's because Hive is NOT a state management tool, if you add/remove something to/from your box, you need to let your state know. You should have a global state that listens to changes from that Box.
Example with a ValueNotifier.
class ItemsNotifier extends ValueNotifier<List<Item>> {
ItemsNotifier() : super(getItemsFromBox());
List<Item> _items = getItemsFromBox();
#override
List<Item> get value => _items;
void addItem(Item item) {
addItemToBox(item);
_items = getItemsFromBox();
notifyListeners();
}
Future<void> deleteAll() async {
await clearBox();
_items = getItemsFromBox();
notifyListeners();
}
}
List<Item> getItemsFromBox() {
return Hive.box<Item>('items').values.toList();
}
Future<void> addItemToBox(Item item) async {
await Hive.box<Item>('items').add(item);
}
Future<void> clearBox() async {
await Hive.box<Item>('items').clear();
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
#override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
late final ItemsNotifier _notifier;
#override
void initState() {
_notifier = ItemsNotifier();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ExampleWidget(notifier: _notifier),
floatingActionButton: FloatingActionButton(
onPressed: _notifier.deleteAll,
child: const Icon(Icons.delete),
),
);
}
#override
void dispose() {
_notifier.dispose();
super.dispose();
}
}
class ExampleWidget extends StatelessWidget {
const ExampleWidget({required this.notifier, Key? key}) : super(key: key);
final ItemsNotifier notifier;
#override
Widget build(BuildContext context) {
return ValueListenableBuilder<List<Item>>(
valueListenable: notifier,
builder: (context, items, _) {
return ListView.builder(
itemBuilder: (context, index) => ListTile(
title: Text(items[index].title),
),
itemCount: items.length,
);
},
);
}
}
Note that if you have multiple pages using the data from this box, I would recommend using something like Provider and having a ChangeNotifier instead of the ValueNotifier or using flutter_bloc and having a Repository that handles communication with Hive.
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.
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!');
}
}