Hi I'm new to Flutter Dart.
In my project I get the excpetion: The method 'findRenderObject' was called on null. Receiver: null Tried calling: findRenderObject()
This happens when I'm firing the ElevatedButton in my main.dart file.
The function that will be fired is the startGame function in my play_field.dart file.
Anyone an idea why I get this exception when firing this button?
The StackTrace:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
#1 PlayFieldState.startGame
package:woost_games/…/game/play_field.dart:74
#2 _TetrisState.build.<anonymous closure>
package:woost_games/main.dart:88
#3 _InkResponseState._handleTap
package:flutter/…/material/ink_well.dart:991
#4 GestureRecognizer.invokeCallback
package:flutter/…/gestures/recognizer.dart:182
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#dbf10
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(332.0, 284.3)
finalLocalPosition: Offset(33.1, 7.9)
button: 1
sent tap down
See my code below:
Main.dart
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'widgets/block/block.dart';
import 'widgets/block/next_block.dart';
import 'widgets/game/play_field.dart';
import 'widgets/game/score_bar.dart';
void main() => runApp(
ChangeNotifierProvider(
create: (context) => Data(),
child: WoostGames()
)
);
class WoostGames extends StatelessWidget {
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
return MaterialApp(home: Tetris());
}
}
class Tetris extends StatefulWidget {
#override
State<StatefulWidget> createState() => _TetrisState();
}
class _TetrisState extends State<Tetris> {
GlobalKey<PlayFieldState> _playFieldKey = GlobalKey();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Woost Tetris',
style: TextStyle(
color: Colors.black,
fontFamily: 'Neue Montreal',
fontSize: 25,
fontWeight: FontWeight.w700
)
),
centerTitle: true,
backgroundColor: Colors.yellow,
),
backgroundColor: Colors.yellow,
body: SafeArea(
child: Column(children: <Widget>[
ScoreBar(),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Flexible(
flex: 3,
child: Padding(
padding: EdgeInsets.fromLTRB(5, 0, 2.5, 5),
child: PlayField(key: _playFieldKey),
)
),
Flexible(
child: Padding(
padding: EdgeInsets.fromLTRB(2.5, 0, 5, 5),
child: Column(
children: <Widget>[
NextBlock(),
SizedBox(height: 30),
ElevatedButton(
child: Text(
Provider.of<Data>(context, listen: false).inGame
? 'End'
: 'Start',
style: TextStyle(
fontSize: 18,
color: Colors.yellow
)
),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(Colors.black)
),
onPressed: () {
Provider.of<Data>(context, listen: false).inGame
? _playFieldKey.currentState.endGame()
: _playFieldKey.currentState.startGame();
}
)
]
)
)
),
],
),
)
])
)
);
}
}
class Data with ChangeNotifier {
int score = 0;
bool inGame = false;
Block nextBlock;
void setScore(score) {
this.score = score ;
notifyListeners();
}
void addScore(score) {
this.score += score;
notifyListeners();
}
void setInGame(inGame) {
this.inGame = inGame;
notifyListeners();
}
void setNextBlock(Block nextBlock) {
this.nextBlock = nextBlock;
notifyListeners();
}
Widget getNextBlockWidget() {
if (!inGame) return Container();
var width = nextBlock.width;
var height = nextBlock.height;
var color;
List<Widget> columns = [];
for (var y = 0; y < height; y++) {
List<Widget> rows = [];
for (var x = 0; x < width; ++ x) {
color = (nextBlock.subBlocks
.where((subBlock) => subBlock.x == x && subBlock.y == y)
.length > 0) ? nextBlock.color : Colors.transparent;
rows.add(Container(width: 12, height: 12, color: color));
}
columns.add(Row(
mainAxisAlignment: MainAxisAlignment.center,
children: rows
));
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: columns,
);
}
}
play_field.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../main.dart';
import '../block/block.dart';
import '../block/blocks.dart';
import '../block/sub_block.dart';
enum Collision {
LANDED_ON_BOTTOM,
LANDED_ON_BLOCK,
HIT_WALL,
HIT_BLOCK,
NONE
}
const PLAY_FIELD_WIDTH = 10;
const PLAY_FIELD_HEIGHT = 20;
const REFRESH_RATE = 300;
const GAME_AREA_BORDER_WIDTH = 2.0;
const SUB_BLOCK_EDGE_WIDTH = 2.0;
class PlayField extends StatefulWidget {
PlayField({Key key}) : super(key: key);
#override
State<StatefulWidget> createState() => PlayFieldState();
}
class PlayFieldState extends State<PlayField> {
bool isGameOver = false;
double subBlockWidth;
Duration blockMovementDuration = Duration(milliseconds: REFRESH_RATE);
GlobalKey _playFieldAreaKey = GlobalKey();
BlockMovement action;
Block block;
Timer timer;
List<SubBlock> oldSubBlocks;
Block getNewBlock() {
int blockType = Random().nextInt(7);
int orientationIndex = Random().nextInt(4);
switch (blockType) {
case 0:
return IBlock(orientationIndex);
case 1:
return JBlock(orientationIndex);
case 2:
return LBlock(orientationIndex);
case 3:
return OBlock(orientationIndex);
case 4:
return TBlock(orientationIndex);
case 5:
return SBlock(orientationIndex);
case 6:
return ZBlock(orientationIndex);
default:
return null;
}
}
void startGame() {
Provider.of<Data>(context, listen: false).setInGame(true);
Provider.of<Data>(context, listen: false).setScore(0);
oldSubBlocks = [];
RenderBox renderBoxPlayField = _playFieldAreaKey.currentContext.findRenderObject();
subBlockWidth = (renderBoxPlayField.size.width - GAME_AREA_BORDER_WIDTH * 2) / PLAY_FIELD_WIDTH;
Provider.of<Data>(context, listen: false).setNextBlock(getNewBlock());
block = getNewBlock();
timer = Timer.periodic(blockMovementDuration, onPlay);
}
void endGame() {
Provider.of<Data>(context, listen: false).setInGame(false);
timer.cancel();
}
void onPlay(Timer timer) {
var status = Collision.NONE;
setState(() {
if (action != null) {
if (!checkOnEdge(action)) {
block.move(action);
}
}
oldSubBlocks.forEach((oldSubBlock) {
block.subBlocks.forEach((subBlock) {
var x = block.x + subBlock.x;
var y = block.y + subBlock.y;
if (x == oldSubBlock.x && y == oldSubBlock.y) {
switch (action) {
case BlockMovement.LEFT:
block.move(BlockMovement.RIGHT);
break;
case BlockMovement.RIGHT:
block.move(BlockMovement.LEFT);
break;
case BlockMovement.ROTATE_CLOCKWISE:
block.move(BlockMovement.ROTATE_COUNTER_CLOCKWISE);
break;
default:
break;
}
}
});
});
if (!checkAtBottom()) {
if (!checkOnBlock()) {
block.move(BlockMovement.DOWN);
} else {
status = Collision.LANDED_ON_BLOCK;
}
} else {
status = Collision.LANDED_ON_BOTTOM;
}
if (status == Collision.LANDED_ON_BLOCK && block.y < 0) {
isGameOver = true;
endGame();
}
else if (status == Collision.LANDED_ON_BOTTOM
|| status == Collision.LANDED_ON_BLOCK) {
block.subBlocks.forEach((subBlock) {
subBlock.x += block.x;
subBlock.y += block.y;
oldSubBlocks.add(subBlock);
});
}
block = Provider.of<Data>(context, listen: false).nextBlock;
Provider.of<Data>(context, listen: false).setNextBlock(getNewBlock());
});
action = null;
updateScore();
}
void updateScore() {
var combo = 1;
Map<int, int> rows = Map();
List<int> rowsToBeRemoved = [];
oldSubBlocks?.forEach((oldSubBlock) {
rows.update(oldSubBlock.y, (value) => ++value, ifAbsent: () => 1);
});
rows.forEach((rowNum, count) {
if (count == PLAY_FIELD_WIDTH) {
Provider.of<Data>(context, listen: false).setScore(combo++);
rowsToBeRemoved.add(rowNum);
}
});
if (rowsToBeRemoved.length > 0)
removeRows(rowsToBeRemoved);
}
void removeRows(List<int> rows) {
rows.sort();
rows.forEach((row) {
oldSubBlocks.removeWhere((oldSubBlock) => oldSubBlock.y == row);
oldSubBlocks.forEach((oldSubBlock) {
if (oldSubBlock.y < row)
++oldSubBlock.y;
});
});
}
bool checkAtBottom() {
return block.y + block.height == PLAY_FIELD_HEIGHT;
}
bool checkOnBlock() {
oldSubBlocks.forEach((oldSubBlock) {
block.subBlocks.forEach((subBlock) {
var x = block.x + subBlock.x;
var y = block.y + subBlock.y;
if (x == oldSubBlock.x && y + 1 == oldSubBlock.y)
return true;
});
});
return false;
}
bool checkOnEdge(BlockMovement action) {
return (action == BlockMovement.LEFT && block.x <= 0) ||
(action == BlockMovement.RIGHT && block.x + block.width >= PLAY_FIELD_WIDTH);
}
Widget getPositionedSquareContainer(Color color, int x, int y) {
return Positioned(
left: x * subBlockWidth,
top: y * subBlockWidth,
child: Container(
width: subBlockWidth - SUB_BLOCK_EDGE_WIDTH,
height: subBlockWidth - SUB_BLOCK_EDGE_WIDTH,
decoration: BoxDecoration(
color: color,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(2.5))
)
)
);
}
Widget drawBlock() {
if (block == null) return null;
List<Positioned> subBlocks = [];
block.subBlocks.forEch((subBlock) {
subBlocks.add(getPositionedSquareContainer(
subBlock.color, subBlock.x + block.x, subBlock.y + block.y
));
});
oldSubBlocks?.forEach((oldSubBlock) {
subBlocks.add(getPositionedSquareContainer(
oldSubBlock.color, oldSubBlock.x, oldSubBlock.y
));
});
if (isGameOver) {
subBlocks.add(getGameOverRect());
}
return Stack(children: subBlocks);
}
Widget getGameOverRect() {
return Positioned(
child: Container(
width: subBlockWidth * 8,
height: subBlockWidth * 3,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5)),
color: Colors.black
),
child: Text(
'Game Over',
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
color: Colors.yellow
)
)
),
left: subBlockWidth * 1,
top: subBlockWidth * 6
);
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onHorizontalDragUpdate: (event) {
action = (event.delta.dx < 1)
? BlockMovement.LEFT
: BlockMovement.RIGHT;
},
onTap: () {
action = BlockMovement.ROTATE_CLOCKWISE;
},
child: AspectRatio(
aspectRatio: PLAY_FIELD_WIDTH / PLAY_FIELD_HEIGHT,
child: Container(
decoration: BoxDecoration(
color: Colors.yellow,
border: Border.all(
width: GAME_AREA_BORDER_WIDTH,
color: Colors.black
),
borderRadius: BorderRadius.all(Radius.circular(2.5))
),
),
)
);
}
}
=== EDIT ====
Screenshot of full exception:
Related
import 'dart:convert';
import 'dart:math';
import "dart:async";
import 'package:flutter/material.dart';
import 'package:quizapp/style.dart';
class loadjson extends StatelessWidget {
String Name = "";
loadjson(this.Name);
String assetload = "";
setasset(){
if(Name == "Python"){
assetload = "assets/python.json";
}
else if(Name == "Java"){
assetload = "assets/java.json";
}
else if(Name == "C++"){
assetload = "assets/cpp.json";
}
else if(Name == "C"){
assetload = "assets/c.json";
}
else if(Name == "Javascript"){
assetload = "assets/js.json";
}
else if(Name == "Linux"){
assetload = "assets/linux.json";
}
else{
assetload = "assets/aws.json";
}
}
#override
Widget build(BuildContext context) {
setasset();
return FutureBuilder(
future: DefaultAssetBundle.of(context).loadString(assetload, cache: false),
builder: (context, snapshot){
List mydata = json.decode(snapshot.data.toString());
// ignore: unnecessary_null_comparison
if(mydata == null){
return Scaffold(
body: Center(
child: Text("Loading....",style: TextStyle(fontFamily: "Sweet Husky ", fontWeight: FontWeight.w700, fontSize: 20.0),),
),
);
}
else{
return quiz(mydata: mydata);
}
},
);
}
}
class quiz extends StatefulWidget {
final List mydata;
const quiz({Key? key, required this.mydata}) : super(key: key);
#override
// ignore: no_logic_in_create_state
State<quiz> createState() => _quizState(mydata);
}
class _quizState extends State<quiz> {
final List mydata;
_quizState(this.mydata);
Color Colortoshow = Apppstyle.acent;
Color right = Apppstyle.right;
Color wrong = Apppstyle.wrong;
int marks = 0;
bool disableAnswer = false;
int j = 1;
int i =0;
int timer = 30;
String Showtimer = "30";
var random_array;
Map<String, Color> btncolor = {
"a": Apppstyle.acent,
"b": Apppstyle.acent,
"c": Apppstyle.acent,
"d": Apppstyle.acent
};
#override
void initState() {
Startimer();
genrandomarray();
// TODO: implement initState
super.initState();
}
bool canceltime = false;
genrandomarray(){
var distinids = [];
var rand = new Random();
for(int i =0; ;){
distinids.add(rand.nextInt(10));
random_array = distinids.toSet().toList();
if(random_array.length < 10){
continue;
}
else{
break;
}
}
print(random_array);
}
void Startimer() async{
const onsec = Duration(seconds: 1);
Timer.periodic(onsec, (Timer T) {
setState(() {
if(timer < 1){
T.cancel();
nextquestion();
}
else if(canceltime == true){
timer = timer - 1;
}
Showtimer = timer.toString();
});
});
}
void nextquestion(){
canceltime = false;
timer = 30;
setState(() {
if(j < 10){
i = random_array[i];
j++;
}
else{
btncolor["a"] = Apppstyle.acent;
btncolor["b"] = Apppstyle.acent;
btncolor["c"] = Apppstyle.acent;
btncolor["d"] = Apppstyle.acent;
disableAnswer = false;
Startimer();
}
});
}
void Checkanswer(String K){
if(mydata[2][i.toString()] == mydata[1][i.toString()][K]){
marks = marks+5;
Colortoshow = right;
}
else{
Colortoshow = wrong;
}
setState(() {
btncolor[K] = Colortoshow;
canceltime = true;
disableAnswer = false;
});
Timer(Duration(seconds: 3), nextquestion);
}
#override
void setState(fn){
if(mounted){
super.setState(() {
fn;
});
}
}
Widget Choicebutton(String K){
return Padding(
padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
child: MaterialButton(
onPressed:() => Checkanswer(K),
color:btncolor[K],
child: Text(mydata[i][i.toString()][K], style: TextStyle(fontFamily: "Eriega", fontSize: 18.0), maxLines: 1,),
minWidth: 200.0,
height: 45.0,
splashColor: Apppstyle.splash,
highlightColor: Apppstyle.splash,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
),
);
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop:() async => false,
child: Scaffold(
body: Column(
children: [
Expanded(
flex: 3,
child: Container(
padding: EdgeInsets.all(15.0),
alignment: Alignment.bottomLeft,
child: Text(mydata[0][i.toString()],style: TextStyle(fontFamily: " Eriega", fontSize: 16.0),),
),
),
Expanded(
flex:6,
child: AbsorbPointer(
absorbing: disableAnswer,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Choicebutton('a'),
Choicebutton('b'),
Choicebutton('c'),
Choicebutton('d'),
],
),
),
)
],
),
)
);
}
}
and the code For Appstyle class
import 'package:flutter/Material.dart';
class Apppstyle{
static Color cardColor = Color(0xFF80DEEA);
static Color right = Color(0xFF2E7D32);
static Color wrong = Color(0xFFF44336);
static Color acent = Color(0xFF18FFFF);
static Color splash = Color(0xFF4DD0E1);
}
Error Shown while building
type 'Null' is not a subtype of 'String'
The relevant error-causing widget was quiz
I am building a multipage quiz App using json file stored locally
but when I try to load json file using futurebuilder in flutter
It Shows error while type Null is not a subtype of Type String
The relevant error-causing widget was quiz i am trying to display the json data when the
user tab a Inkwell
Hi i have a riveanimation that changes flower when the user has checkin, however the page of the animation would not change state once build as it is in a bottomnavigationbar. Is there anyway to reload the page to change the state of the animation. I only want to reload only one page and maintain the others.
This is the animation picture, it should change the leaf to green
here is my bottomnavigationbar page
class bottomnavbar extends StatefulWidget {
const bottomnavbar ({Key? key}) : super(key: key);
#override
_bottomnavbarState createState() => _bottomnavbarState();
}
class _bottomnavbarState extends State<bottomnavbar> {
double screenHeight = 0;
double screenWidth = 0;
String currentDate = DateFormat('yyyy-MM-dd').format(DateTime.now());
Color primary = const Color(0xffeef444c);
int currentIndex = 0;
List<IconData> navigationIcons = [
FontAwesomeIcons.personPraying,
FontAwesomeIcons.leaf,
FontAwesomeIcons.dice,
FontAwesomeIcons.person,
];
#override
Widget build(BuildContext context) {
screenHeight = MediaQuery.of(context).size.height;
screenWidth = MediaQuery.of(context).size.width;
print("Current save date is ${Provider.of<checkinlist>(context, listen: false).checkDate()}");
if( Provider.of<checkinlist>(context, listen: false).checkDate() == 'not set'){
Provider.of<checkinlist>(context, listen: false).saveDatenow();
}
else if (Provider.of<checkinlist>(context, listen: false).checkDate() != currentDate ){
Provider.of<checkinlist>(context, listen: false).overwriteSaveDate();
Provider.of<checkinlist>(context, listen: false).dailyreset();
}
else if (Provider.of<checkinlist>(context, listen: false).checkDate() == currentDate ){
}
return Scaffold(
backgroundColor: Colors.blueGrey[900],
body: IndexedStack(
index: currentIndex,
children: const [
mainmenu(),
FlowerGarden(),
Gamespage(),
ProfilePage(),
],
),
bottomNavigationBar: Container(
height: 70,
margin: const EdgeInsets.only(
left: 12,
right: 12,
bottom: 24,
),
decoration: const BoxDecoration(
color: Colors.lime,
borderRadius: BorderRadius.all(Radius.circular(40)),
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 10,
offset: Offset(2, 2),
),
],
),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(40)),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
for(int i = 0; i < navigationIcons.length; i++)...<Expanded>{
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
currentIndex = i;
});
},
child: Container(
height: screenHeight,
width: screenWidth,
color: Colors.deepPurple[900],
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
navigationIcons[i],
color: i == currentIndex ? primary : Colors.white60,
size: i == currentIndex ? 30 : 26,
),
i == currentIndex ? Container(
margin: const EdgeInsets.only(top: 6),
height: 3,
width: 22,
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(40)),
color: primary,
),
) : const SizedBox(),
],
),
),
),
),
),
}
],
),
),
),
);
}
}
And this is my riveanimation page
class FlowerGarden extends StatefulWidget{
const FlowerGarden({Key? key,
}) : super(key: key);
#override
_SimpleFlowerGardenState createState() => _SimpleFlowerGardenState();
}
final myCoordinates = Coordinates(3.139003, 101.686855);
// Replace with your own location lat, lng.
final params = CalculationMethod.karachi.getParameters();
final prayerTimes = PrayerTimes.today(myCoordinates, params);
class _SimpleFlowerGardenState extends State<FlowerGarden>{
SMITrigger? _SubuhEarly;
SMITrigger? _SubuhLate;
SMITrigger? _ZohorEarly;
SMITrigger? _ZohorLate;
SMITrigger? _AsarEarly;
SMITrigger? _AsarLate;
SMITrigger? _MaghribEarly;
SMITrigger? _MaghribLate;
SMITrigger? _IsyakEarly;
SMITrigger? _IsyakLate;
SMITrigger? _solatPerfect;
SMITrigger? _solatImperfect;
Artboard? _riveArtboard;
String? message;
#override
void initState() {
super.initState();
// Load the animation file from the bundle, note that you could also
// download this. The RiveFile just expects a list of bytes.
rootBundle.load('android/assets/lotus.riv').then(
(data) async {
// Load the RiveFile from the binary data.
final file = RiveFile.import(data);
// The artboard is the root of the animation and gets drawn in the
// Rive widget.
final artboard = file.mainArtboard;
var controller =
StateMachineController.fromArtboard(artboard, 'State Machine ',onStateChange: _onStateChange);
if (controller != null) {
artboard.addController(controller);
_SubuhEarly = controller.findInput<bool>('Subuh Early') as SMITrigger;
_SubuhLate = controller.findInput<bool>('Subuh Late') as SMITrigger;
_ZohorEarly = controller.findInput<bool>('Zohor Early') as SMITrigger;
_ZohorLate = controller.findInput<bool>('Zohor late') as SMITrigger;
_AsarEarly = controller.findInput<bool>('Asar Early') as SMITrigger;
_AsarLate = controller.findInput<bool>('Asar Late') as SMITrigger;
_MaghribEarly = controller.findInput<bool>('Maghrib early') as SMITrigger;
_MaghribLate = controller.findInput<bool>('Maghrib late') as SMITrigger;
_IsyakEarly = controller.findInput<bool>('Ishak early') as SMITrigger;
_IsyakLate = controller.findInput<bool>('Ishak late') as SMITrigger;
_solatPerfect = controller.findInput<bool>('Solat Perfect') as SMITrigger;
_solatImperfect = controller.findInput<bool>('Solat Imperfect') as SMITrigger;
}
setState(() => _riveArtboard = artboard);
},
);
}
void _onStateChange(
String stateMachineName,
String stateName,
) =>
setState(
() => print('State Changed in $stateMachineName to $stateName') ,
);
void flowercheck(){
if (Provider.of<checkinlist>(context, listen: false).getSubuh() == 2){
_solatImperfect?.fire();
}
else if (Provider.of<checkinlist>(context, listen: false).getZohor() == 2){
_solatImperfect?.fire();
}
else if (Provider.of<checkinlist>(context, listen: false).getAsar() == 2){
_solatImperfect?.fire();
}
else if (Provider.of<checkinlist>(context, listen: false).getMaghrib() == 2){
_solatImperfect?.fire();
}
else if (Provider.of<checkinlist>(context, listen: false).getIsyak() == 2){
_solatImperfect?.fire();
}
else {
_solatPerfect?.fire();
}
}
void leafcheck(){
int subuh = Provider.of<checkinlist>(context, listen: false).getSubuh();
int zohor = Provider.of<checkinlist>(context, listen: false).getZohor();
int asar = Provider.of<checkinlist>(context, listen: false).getAsar();
int maghrib = Provider.of<checkinlist>(context, listen: false).getMaghrib();
int isyak = Provider.of<checkinlist>(context, listen: false).getIsyak();
double health = Provider.of<solatPoints>(context, listen: false).getHealth();
switch(subuh){
case 1:
_SubuhEarly?.fire();
break;
case 2:
_SubuhLate?.fire();
if (health > 5) {
Provider.of<solatPoints>(context, listen: false).decreasehealth();
}
break;
}
switch(zohor){
case 1:
_ZohorEarly?.fire();
break;
case 2:
_ZohorLate?.fire();
if (health > 5) {
Provider.of<solatPoints>(context, listen: false).decreasehealth();
}
break;
}
switch(asar){
case 1:
_AsarEarly?.fire();
break;
case 2:
_AsarLate?.fire();
if (health > 5) {
Provider.of<solatPoints>(context, listen: false).decreasehealth();
}
break;
}
switch(maghrib){
case 1:
_MaghribEarly?.fire();
break;
case 2:
_MaghribLate?.fire();
if (health > 5) {
Provider.of<solatPoints>(context, listen: false).decreasehealth();
}
break;
}
switch(isyak){
case 1:
_IsyakEarly?.fire();
break;
case 2:
_IsyakLate?.fire();
if (health > 5) {
Provider.of<solatPoints>(context, listen: false).decreasehealth();
}
break;
default:
break;
}
}
#override
Widget build(BuildContext context) {
setState(() {
leafcheck();
});
return Scaffold(
backgroundColor: Colors.blueGrey[900],
appBar: AppBar(
backgroundColor: Colors.deepPurple[900],
title: const Text('Bunga Solat'),
),
body: Center(
child: GestureDetector(
child: _riveArtboard == null
? const SizedBox()
: Stack(
children: [
Positioned.fill(
child: Rive(
artboard: _riveArtboard!,
),
),
]
),
),
)
);
}
}
class ClassProfile extends State<ClassProfile> with AutomaticKeepAliveClientMixin {
#override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => false;
#override
Widget build(BuildContext context) {
if(wantKeepAlive) {
super.build(context);
}
.
.
.
.
first set - with AutomaticKeepAliveClientMixin
then, override wantkeepalive variable
at last, defined super.build with if condition in your main widget builder function
In future if you want to persist state of your class then set wantKeepAlive => true
I am working on this navigation that is a list but it is very inconstant in refreshing. Sometimes it refreshes and appears the new button, and other times it does not show the new button. Have you had this problem before.
This is my page showing the routers list and the pages list:
class RouterTabletPage extends StatefulWidget { // ignore: must_be_immutable
BuildContext? context;
RouterTabletPage({Key? key}) : super(key: key);
#override
_RouterTabletPageState createState() => _RouterTabletPageState();
}
class _RouterTabletPageState extends State<RouterTabletPage> with SingleTickerProviderStateMixin {
final NavigationController _navigationController = IoC.get<NavigationController>()!;
final TopBarWidgetController _topBarController = IoC.get<TopBarWidgetController>()!;
final TerminalLockController _terminalLockController = IoC.get<TerminalLockController>()!;
final PreAlarmController _preAlarmController = IoC.get<PreAlarmController>()!;
final UpdateUiService _updateUiService = IoC.get<UpdateUiService>()!;
PageController? _pageViewController;
#override
initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((_) async {
await _navigationController.initRoutes();
_navigationController.pageViewController = PageController(initialPage: _navigationController.menuSelectedIndex);
_pageViewController = _navigationController.pageController;
await _navigationController.getTabs(_navigationController.menuSelectedIndex);
await _navigationController.setListenerPage();
_topBarController.initTopBar();
await _navigationController.refreshCycleBtn();
});
}
registerContext() {
widget.context = context;
_terminalLockController.registerContext(context);
_preAlarmController.registerContext(context);
_updateUiService.registerContext(context);
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
registerContext();
return Observer(
builder: (_) {
return Scaffold(
appBar: _topBarController.topBarWidget,
backgroundColor: Theme.of(context).primaryColor,
body: Container(
color: Theme.of(context).primaryColor,
margin: const EdgeInsets.all(0.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
width: 60,
margin: const EdgeInsets.only(top: 0.0),
color: Theme.of(context).primaryColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: _navigationController.tabs,
),
),
Expanded(
child: Container(
color: Theme.of(context).backgroundColor,
child: PageView(
scrollDirection: Axis.vertical,
controller: _pageViewController,
children: _navigationController.pages,
),
And this is my controller:
#observable
Map<num, PageWithRouter> routers = {};
#observable
String router2 = "";
#observable
int menuSelectedIndex = 0;
#observable
String preloader = "";
#observable
Object? arguments;
#observable
List<Widget> pages = [];
#observable
PageController? pageController;
#observable
List<Widget> tabs = [];
String pathPage = "";
#observable
bool cycleEnable = true;
#observable
bool isCycleStarted = false;
#observable
BuildContext? context;
set pageViewController(PageController _pageController) =>
pageController = _pageController;
#action
Future setRouter(String path) async {
initRoutes();
if (router2 != path) {
pathPage = path;
if (path != pathSplashScreen && path != "") router2 = path;
if (router2 != pathRegister) {
await _treatAuth().then((value) async {
if (!value) {
setMenuSelectedByPath(path);
await initPages();
}
});
}
}
}
#action
Future refreshCurrentPage(List<String> path) async {
if (path.contains(router2)) {
await _treatAuth().then((value) async {
if (!value) {
await initPages();
}
});
}
}
#action
setRouterByHomePage(String path) async {
await setRouter(path);
// log('path'+path);
pageController!.jumpToPage(menuSelectedIndex);
}
#action
Future<void> setRouterByIndex(int index) async {
menuSelectedIndex = index;
String _router = routers[menuSelectedIndex]?.defaultRouter ?? " ";
if (_router != router2) {
await setRouter(_router);
}
}
#action
setArguments(Object? obj) {
arguments = obj;
}
#action
setCycleEnable(bool enable) async{
cycleEnable = enable;
log('noaction'+cycleEnable.toString());
}
#action
setCycleStarted(bool started) {
isCycleStarted = started;
}
#action
setMenuSelectedIndex(int index) {
menuSelectedIndex = index;
}
#action
Widget getByIndex(int index) {
return routers[index]?.page ?? const SizedBox.shrink();
}
#action
Future<Widget> getPage(String router, int pageIdentify) async {
if (pageIdentify != menuSelectedIndex) {
return getByIndex(pageIdentify);
}
for (var item in routers.values) {
if (item.routers.any((r) => r == router) ||
(router == "" && item.defaultRouter == pathHome)) {
item.page = _createPageByRouter(router);
return _createPageByRouter(router);
// TODO: strange behaviour where returning item.page is identified as Widget?
}
}
return const SizedBox.shrink();
}
#action
Widget _createPageByRouter(String router) {
bool isTablet = DeviceType.get().isTablet!;
Widget page = const SizedBox.shrink();
bool isShowSubMenu = true;
log('router'+router);
switch (router) {
case '':
case pathHome:
page = isTablet
? const HomeTabletPage(title: "Home")
: const HomePhonePage(title: "Home");
if (isTablet) isShowSubMenu = false;
break;
case pathMessageReceived:
page = isTablet
? MessageListTabletPage(type: 1)
: MessageListPhonePage(type: 1);
break;
case pathMessageSend:
page = isTablet
? MessageListTabletPage(type: 0)
: MessageListPhonePage(type: 0);
break;
case pathInformation:
page = isTablet ? const InformationTabletPage() : const InformationPhonePage();
break;
case pathExternalDevice:
page =
isTablet ? const ExternalDeviceTabletPage() : const ExternalDevicePhonePage();
break;
case pathErrorCode:
page = isTablet ? const ErrorCodeTabletPage() : const ErrorCodePhonePage();
break;
case pathSetting:
page = isTablet
? SettingTabletPage(title: "Configurações", defaultPage: true)
: SettingPhonePage(title: "Configurações", defaultPage: true);
break;
case pathSettingUnitConfig:
page = isTablet
? SettingTabletPage(title: "Configurações", defaultPage: false)
: SettingPhonePage(title: "Configurações", defaultPage: false);
break;
case pathOperationCycle:
if (isTablet) {
return const OperationCycleMasterTablet();
} else {
if (isCycleStarted) {
page = const OperationCyclePhonePage();
} else {
page = const OperationCycleHomePhonePage();
}
}
if (isTablet) isShowSubMenu = false;
break;
default:
page = const SizedBox.shrink();
break;
}
if (isShowSubMenu && isTablet) {
return Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
IoC.get<SubMenu>()!,
Expanded(
child: page,
),
],
);
}
return page;
}
#action
Future initPages() async {
initRoutes();
pages = [];
for (final key in routers.keys) {
if (routers[key]!.defaultRouter ==
NavigationControllerBase.pathOperationCycle &&
cycleEnable == false) {
setMenuSelectedIndex(0);
setRouterByHomePage(pathHome);
}
final page = await getPage(router2, key as int);
pages.add(page);
log('initPages'+ pages.toString());
}
}
Future<bool> _treatAuth() async {
final authStatusService = IoC.get<AuthStatusService>()!;
final authStatus = await authStatusService.syncData();
if (authStatus != null && authStatus.requireLogin) {
pathPage = pathAuthentication;
router2 = pathAuthentication;
IoC.get<NavigationService>()!.navigateWithoutHistory(pathAuthentication);
return true;
}
return false;
}
#action
Future initRoutes() async {
if (DeviceType.get().isPhone!) {
routers = <num, PageWithRouter>{
0: PageWithRouter([pathHome], null),
1: PageWithRouter([pathInformation, pathExternalDevice], null),
2: PageWithRouter([pathErrorCode], null),
3: PageWithRouter([pathSetting, pathSettingUnitConfig], null),
};
if (cycleEnable) {
routers.addAll({
4: PageWithRouter([pathOperationCycle], null),
5: PageWithRouter([pathMessageReceived, pathMessageSend], null),
});
} else {
routers.addAll({
4: PageWithRouter([pathMessageReceived, pathMessageSend], null),
});
}
} else {
routers = <num, PageWithRouter>{
0: PageWithRouter([pathHome], null),
1: PageWithRouter([pathMessageReceived, pathMessageSend], null),
2: PageWithRouter([pathInformation, pathExternalDevice], null),
3: PageWithRouter([pathErrorCode], null),
4: PageWithRouter([pathSetting, pathSettingUnitConfig], null),
};
if (cycleEnable == true) {
routers.addAll({
5: PageWithRouter([pathOperationCycle], null)
});
log('initRoutes'+routers.toString());
}
}
}
#action
Future refreshCycleBtn() async {
tabs = [];
if (DeviceType.get().isPhone!) {
tabs = List.from(tabs..addAll(await getTabsPhone(menuSelectedIndex)));
} else {
tabs = List.from(tabs..addAll(await getTabs(menuSelectedIndex)));
log('tabs'+tabs.toString());
}
await initRoutes();
await _verifyOperationCycleEnabled();
if (cycleEnable == false) {
if (menuSelectedIndex > routers.length - 1) {
await setRouterByIndex(0);
pageController!.jumpToPage(0);
} else {
await initPages();
}
} else {
await initPages();
pages = [];
List<Widget> newPages = [];
for (final key in routers.keys) {
final page = await getPage(router2, key as int);
newPages.add(page);
pages = List.from(pages..addAll(newPages));
}
if (menuSelectedIndex > routers.length - 1) {
await setRouterByIndex(0);
} else {
await initRoutes();
await initPages();
}
}
}
Future<List<Widget>> getTabs(int index) async {
List<Widget> newTabs = [];
//await _menuIconsController.refreshAllBadge();
for (final key in routers.keys) {
if (routers[key]!.defaultRouter ==
NavigationControllerBase.pathOperationCycle && cycleEnable == false) {
continue;
}
newTabs.add(
SizedBox(
height: 70,
child: TextButton(
style: TextButton.styleFrom(
backgroundColor: index == key ? AppTheme.theme.colorScheme.secondary : Colors.transparent,
shape: const ContinuousRectangleBorder(
borderRadius: BorderRadius.all(Radius.zero),
),
),
child: _getTabIcon(routers[key]!.defaultRouter),
onPressed: () async {
await setRouterByIndex(key as int);
pageController!.jumpToPage(key);
},
),
),
);
}
tabs = [];
tabs = List.from(tabs..addAll(newTabs));
log('newtabs'+newTabs.toString());
return newTabs;
}
Widget _getTabIcon(String router) { // os cases com icons
switch (router) {
case '': //setado para home
return const Icon(
Icons.home,
color: Colors.white,
size: 30,
);
case NavigationControllerBase.pathHome:
return const Icon(
Icons.home,
key: Key('btn_home_page'),
color: Colors.white,
size: 30,
);
case NavigationControllerBase.pathMessageReceived:
var icon = const Icon(
Icons.mail,
key: Key('btn_message_page'),
color: Colors.white,
size: 30,
);
return Observer(builder: (_) {
if (_menuIconsController.countMessages == 0) return icon;
return Badge(
badgeContent: Text(
_menuIconsController.countMessages.toString(),
style: const TextStyle(
color: Colors.white,
),
),
child: icon,
);
});
case NavigationControllerBase.pathInformation:
return const Icon(
Icons.info,
key: Key('btn_information_page'),
color: Colors.white,
size: 30,
);
case NavigationControllerBase.pathErrorCode:
var icon = const Icon(
Icons.warning,
key: Key('btn_erro_code_page'),
color: Colors.white,
);
return Observer(builder: (_) {
if (_menuIconsController.countUccErrors == 0) return icon;
return Badge(
badgeContent: Text(
_menuIconsController.countUccErrors.toString(),
style: const TextStyle(
color: Colors.white,
),
),
child: icon,
);
});
case NavigationControllerBase.pathSetting:
var icon = const Icon(
Icons.settings,
key: Key('btn_setting_page'),
color: Colors.white,
size: 30,
);
if (!_menuIconsController.showBadgetSetting!) return icon;
return Observer(builder: (_) {
return Badge(
badgeContent: const Padding(
padding: EdgeInsets.all(2.0),
child: Text(
"!",
style: TextStyle(
color: Colors.white,
),
),
),
child: icon,
);
});
case NavigationControllerBase.pathOperationCycle:
return const Icon(
MtfIcons.operationalCycleHomeMenu,
key: Key('btn_operation_cycle'),
color: Colors.white,
size: 30,
);
default:
return const SizedBox.shrink();
}
}
Future<List<Widget>> getTabsPhone(int index) async {
List<Widget> newTabs = [];
//await _menuIconsController.refreshAllBadge();
for (final key in routers.keys) {
if (routers[key]!.defaultRouter ==
NavigationControllerBase.pathOperationCycle &&
cycleEnable == false) {
continue;
}
newTabs.add(
Expanded(
child: TextButton(
style: TextButton.styleFrom(
primary: index == key ? AppTheme.theme.colorScheme.secondary : Colors.transparent,
),
onPressed: () async {
await setRouterByIndex(key as int);
pageController!.jumpToPage(key);
},
child: _getTabIcon(routers[key]!.defaultRouter),
),
),
);
}
tabs = [];
tabs = List.from(tabs..addAll(newTabs));
return newTabs;
}
Future setListenerPage() async {
// TODO: refactor this lambda;
var listener = () async { // ignore: prefer_function_declarations_over_variables
if (DeviceType.get().isPhone!) {
await getTabsPhone(menuSelectedIndex);
} else {
await getTabs(menuSelectedIndex);
}
double page = pageController!.page!;
await setRouterByIndex(page.round());
};
pageController!.removeListener(listener);
pageController!.addListener(listener);
}
void setMenuSelectedByPath(String? path) { //init menuSeletecteIndex =id
routers.forEach((id, page) {
if (page.routers.any((r) => r == path)) {
menuSelectedIndex = id as int;
}
});
}
Future _verifyOperationCycleEnabled() async { //setCycleEnable true || false
final _operationCycleService = IoC.get<OperationCycleService>()!;
final operationCycle = await _operationCycleService.syncData();
if (operationCycle == null) {
setCycleEnable(false);
} else {
setCycleEnable(true);
}
}
}
class PageWithRouter {
List<String> routers;
Widget? page;
String get defaultRouter => routers.first;
PageWithRouter(this.routers, this.page);
}
I have already tried changing WidgetsBinding to Future.delay and tried initiating different ways inside refreshCycleBtn().
I understand that the problem is in a lifecircle that I'm trying to set a state in Provider before the Widget is rendered but where can I do that. Only in a Container Widget? But I cannot do that unless I've a button or something.
I hope you got the issue of the problem here.
I would appreciate any hints!
my Error:
setState() or markNeedsBuild() called during build.
or
The setter 'lastPage=' was called on null.
Receiver: null
Tried calling: lastPage=true
if I set the state in here
_detectLastPage() {
int currentPage = this.currentStep == null ? 1 : this.currentStep + 1;
if (currentPage == 1 && this.currentStep == null) {
this._onFirstPage();
} else if (currentPage == this.totalSteps) {
this.lastPage = true;
_welcomeBloc.lastPage = true;
this._onLastPage();
} else {
this.lastPage = false;
_welcomeBloc.lastPage = true;
}
}
My Widget:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:ui_flutter/screens/welcome/welcome_bloc.dart';
class Footer extends StatelessWidget {
final int currentStep;
final int totalSteps;
final Color activeColor;
final Color inactiveColor;
final Duration duration;
final Function onFinal;
final Function onStart;
final double radius = 10.0;
final double distance = 4.0;
Container stepper;
Container nextArrow;
bool lastPage;
WelcomeBloc _welcomeBloc;
Footer({
this.activeColor,
this.inactiveColor,
this.currentStep,
this.totalSteps,
this.duration,
this.onFinal,
this.onStart,
}) {
this._detectLastPage();
this._makeStepper();
this._makeNextArrow();
}
#override
Widget build(BuildContext context) {
print('footer is launching');
final WelcomeBloc _welcome = Provider.of<WelcomeBloc>(context);
_welcomeBloc = _welcome;
// this._welcomeBloc.lastPage = true; // I'd like to set the state here
return Container(
alignment: Alignment.bottomCenter,
padding: EdgeInsets.symmetric(vertical: 30.0, horizontal: 30.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
this.stepper,
this.nextArrow,
RaisedButton(
child: Text('kdfljds'),
onPressed: () {
print(_welcomeBloc.lastPage);
_welcomeBloc.lastPage = true; // I can access from here BUT CANNOT access outside this container
},
)
],
),
);
}
_makeCirle(activeColor, inactiveColor, position, currentStep) {
currentStep = currentStep ?? 0;
Color color = (position == currentStep) ? activeColor : inactiveColor;
return Container(
height: this.radius,
width: this.radius,
margin: EdgeInsets.only(left: this.distance, right: this.distance),
decoration: BoxDecoration(
color: color,
border: Border.all(color: activeColor, width: 2.0),
borderRadius: BorderRadius.circular(50.0)),
);
}
_makeStepper() {
List<Container> circles = List();
for (var i = 0; i < totalSteps; i++) {
circles.add(
_makeCirle(this.activeColor, this.inactiveColor, i, this.currentStep),
);
}
this.stepper = Container(
child: Row(
children: circles,
),
);
}
_makeNextArrow() {
this.nextArrow = Container(
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: GestureDetector(
onTap: () {
_welcomeBloc.controller.nextPage(
duration: this.duration ?? Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
},
child: Icon(
Icons.arrow_forward,
)),
),
);
}
_onLastPage() {
if (this.onFinal != null) {
this.onFinal();
}
}
_onFirstPage() {
if (this.onStart != null) {
this.onStart();
}
}
_detectLastPage() {
int currentPage = this.currentStep == null ? 1 : this.currentStep + 1;
if (currentPage == 1 && this.currentStep == null) {
this._onFirstPage();
} else if (currentPage == this.totalSteps) {
this.lastPage = true;
this._onLastPage();
} else {
this.lastPage = false;
}
}
}
BlocFile
import 'package:flutter/material.dart';
class WelcomeBloc extends ChangeNotifier {
PageController _controller = PageController();
int _currentPage;
bool _lastPage = false;
bool get lastPage => _lastPage;
set lastPage(bool value){
print(value);
_lastPage = value;
notifyListeners();
}
int get currentPage => _currentPage;
set currentPage(int value) {
_currentPage = value;
notifyListeners();
}
get controller => _controller;
nextPage(Duration duration, Curves curve){
controller.nextPage(duration: duration, curve: curve);
}
}
[![error screen with StateLess, since I use Provider][1]][1]
There I call like this:
_detectLastPage() {
int currentPage =
this.widget.currentStep == null ? 1 : this.widget.currentStep + 1;
if (currentPage == 1 && this.widget.currentStep == null) {
this._onFirstPage();
} else if (currentPage == this.widget.totalSteps) {
this.lastPage = true;
setState(() {
_welcomeBloc.lastPage = true;
});
this._onLastPage();
} else {
this.lastPage = false;
setState(() {
_welcomeBloc.lastPage = false;
});
}
}
And without SetState seem to be the same error...
this error if I call from inside initState from your example. Just forgot you attach it
You cannot use the setState method in a StatelessWidget. Convert it to a StatefulWidget and call the setState in the initState method.
Like this
class Footer extends StatefulWidget {
final int currentStep;
final int totalSteps;
final Color activeColor;
final Color inactiveColor;
final Duration duration;
final Function onFinal;
final Function onStart;
Footer({
this.activeColor,
this.inactiveColor,
this.currentStep,
this.totalSteps,
this.duration,
this.onFinal,
this.onStart,
});
#override
_FooterState createState() => _FooterState();
}
class _FooterState extends State<Footer> {
final double radius = 10.0;
final double distance = 4.0;
Container stepper;
Container nextArrow;
bool lastPage;
WelcomeBloc _welcomeBloc;
#override
void initState(){
this._detectLastPage();
this._makeStepper();
this._makeNextArrow();
final WelcomeBloc _welcome = Provider.of<WelcomeBloc>(context);
_welcomeBloc = _welcome;
setState((){
this._welcomeBloc.lastPage = true; // Where to use setState
});
}
#override
Widget build(BuildContext context) {
print('footer is launching');
return Container(
alignment: Alignment.bottomCenter,
padding: EdgeInsets.symmetric(vertical: 30.0, horizontal: 30.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
this.stepper,
this.nextArrow,
RaisedButton(
child: Text('kdfljds'),
onPressed: () {
print(_welcomeBloc.lastPage);
_welcomeBloc.lastPage = true; // I can access from here BUT CANNOT access outside this container
},
)
],
),
);
}
_makeCirle(activeColor, inactiveColor, position, currentStep) {
currentStep = currentStep ?? 0;
Color color = (position == currentStep) ? activeColor : inactiveColor;
return Container(
height: this.radius,
width: this.radius,
margin: EdgeInsets.only(left: this.distance, right: this.distance),
decoration: BoxDecoration(
color: color,
border: Border.all(color: activeColor, width: 2.0),
borderRadius: BorderRadius.circular(50.0)),
);
}
_makeStepper() {
List<Container> circles = List();
for (var i = 0; i < totalSteps; i++) {
circles.add(
_makeCirle(this.activeColor, this.inactiveColor, i, this.currentStep),
);
}
this.stepper = Container(
child: Row(
children: circles,
),
);
}
_makeNextArrow() {
this.nextArrow = Container(
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: GestureDetector(
onTap: () {
_welcomeBloc.controller.nextPage(
duration: this.duration ?? Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
},
child: Icon(
Icons.arrow_forward,
)),
),
);
}
_onLastPage() {
if (this.onFinal != null) {
this.onFinal();
}
}
_onFirstPage() {
if (this.onStart != null) {
this.onStart();
}
}
_detectLastPage() {
int currentPage = this.currentStep == null ? 1 : this.currentStep + 1;
if (currentPage == 1 && this.currentStep == null) {
this._onFirstPage();
} else if (currentPage == this.totalSteps) {
this.lastPage = true;
this._onLastPage();
} else {
this.lastPage = false;
}
}
}
If I got it right you are trying to simulate PageView navigation by some circle bellow it(Indicators).
To do so there are lots of good resources and also packages like:
This example or this package
But for your code I wrote it in 2 approaches:
First Approach
This one is your code and use provider.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(Home());
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => WelcomeBloc(),
child: Consumer<WelcomeBloc>(
builder: (BuildContext context, value, Widget child) {
PageController controller = value.controller;
print('object');
return MaterialApp(
home: Scaffold(
body: Stack(
children: <Widget>[
PageView(
controller: controller,
children: List.generate(
10, (i) => Center(child: Text('Page $i'))),
onPageChanged: (i) {
value.currentPage = i;
},
),
Footer(
activeColor: Colors.red,
duration: Duration(seconds: 1),
inactiveColor: Colors.yellow,
onFinal: () {},
onStart: () {},
totalSteps: 10,
)
],
),
),
);
},
),
);
}
}
class Footer extends StatefulWidget {
final int totalSteps;
final Color activeColor;
final Color inactiveColor;
final Duration duration;
final Function onFinal;
final Function onStart;
final double radius;
final double distance;
Footer({
this.activeColor,
this.inactiveColor,
this.totalSteps,
this.duration,
this.onFinal,
this.onStart,
this.radius = 10.0,
this.distance = 4.0,
});
#override
_FooterState createState() => _FooterState();
}
class _FooterState extends State<Footer> {
bool lastPage;
WelcomeBloc _welcomeBloc;
#override
Widget build(BuildContext context) {
final WelcomeBloc _welcome = Provider.of<WelcomeBloc>(context);
_welcomeBloc = _welcome;
// this._welcomeBloc.lastPage = true; // I'd like to set the state here
return Container(
alignment: Alignment.bottomCenter,
padding: EdgeInsets.symmetric(vertical: 30.0, horizontal: 30.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
_makeStepper(),
_makeNextArrow(),
],
),
);
}
_makeCircle(activeColor, inactiveColor, position, currentStep) {
currentStep = currentStep ?? 0;
Color color = (position == currentStep) ? activeColor : inactiveColor;
return Container(
height: widget.radius,
width: widget.radius,
margin: EdgeInsets.only(left: widget.distance, right: widget.distance),
decoration: BoxDecoration(
color: color,
border: Border.all(color: activeColor, width: 2.0),
borderRadius: BorderRadius.circular(50.0)),
);
}
_makeNextArrow() {
return Container(
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: GestureDetector(
onTap: () async {
await _welcomeBloc.nextPage(widget.duration, Curves.easeInOut);
setState(() {});
},
child: Icon(
Icons.arrow_forward,
)),
),
);
}
_makeStepper() {
return Container(
child: Row(
children: List.generate(
widget.totalSteps,
(i) => _makeCircle(
this.widget.activeColor,
this.widget.inactiveColor,
i,
_welcomeBloc.currentPage,
),
),
),
);
}
_onLastPage() {
if (this.widget.onFinal != null) {
this.widget.onFinal();
}
}
_onFirstPage() {
if (this.widget.onStart != null) {
this.widget.onStart();
}
}
_detectLastPage() {
int currentPage =
_welcomeBloc.currentPage == null ? 1 : _welcomeBloc.currentPage + 1;
if (currentPage == 1 && _welcomeBloc.currentPage == null) {
this._onFirstPage();
} else if (currentPage == this.widget.totalSteps) {
this.lastPage = true;
this._onLastPage();
} else {
this.lastPage = false;
}
}
}
class WelcomeBloc extends ChangeNotifier {
final PageController _controller = PageController();
int _currentPage = 0;
bool _lastPage = false;
bool get lastPage => _lastPage;
set lastPage(bool value) {
_lastPage = value;
notifyListeners();
}
int get currentPage => _currentPage;
set currentPage(int value) {
_currentPage = value;
notifyListeners();
}
PageController get controller => _controller;
Future<void> nextPage(Duration duration, Curve curve) {
currentPage = controller.page.floor() + 1;
return controller.nextPage(duration: duration, curve: curve);
}
}
Second Approach
In the second one I removed provider stuff because it can be done without it by using PageController features.
import 'package:flutter/material.dart';
void main() {
runApp(Home());
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
PageController controller = PageController(initialPage: 0);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Stack(
children: <Widget>[
PageView(
controller: controller,
children: List.generate(
10,
(i) => Center(child: Text('Page $i')),
),
onPageChanged: (page) {
setState(() {});
},
),
Footer(
currentPage: controller.hasClients ? controller.page.floor() : 0,
activeColor: Colors.red,
inactiveColor: Colors.yellow,
totalSteps: 10,
onTap: () async {
await controller.nextPage(
duration: Duration(seconds: 1) ?? Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
setState(() {});
},
)
],
),
),
);
}
}
class Footer extends StatelessWidget {
final int totalSteps;
final Color activeColor;
final Color inactiveColor;
final double radius;
final double distance;
final int currentPage;
final GestureTapCallback onTap;
Footer({
this.activeColor,
this.inactiveColor,
this.totalSteps,
this.radius = 10.0,
this.distance = 4.0,
this.currentPage,
this.onTap,
});
#override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.bottomCenter,
padding: EdgeInsets.symmetric(vertical: 30.0, horizontal: 30.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
_makeStepper(),
_makeNextArrow(),
],
),
);
}
_makeCircle(activeColor, inactiveColor, position) {
Color color = (position == currentPage) ? activeColor : inactiveColor;
return Container(
height: radius,
width: radius,
margin: EdgeInsets.only(left: distance, right: distance),
decoration: BoxDecoration(
color: color,
border: Border.all(color: activeColor, width: 2.0),
borderRadius: BorderRadius.circular(50.0)),
);
}
_makeNextArrow() {
return Container(
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: GestureDetector(
onTap: onTap,
child: Icon(
Icons.arrow_forward,
)),
),
);
}
_makeStepper() {
return Container(
child: Row(
children: List.generate(
totalSteps,
(i) => _makeCircle(
this.activeColor,
this.inactiveColor,
i,
),
),
),
);
}
}
So, the solution of my error is in didChangeDependencies hook.
I tried to change the state above at the very moment when the Widget was being built (that's how I got it).
So, I just needed to run it either before or after widget is built.
That's how it looks like in the code:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:ui_flutter/screens/welcome/welcome_bloc.dart';
import 'package:flutter/scheduler.dart';
class Footer extends StatefulWidget {
final int currentStep;
final int totalSteps;
final Color activeColor;
final Color inactiveColor;
final Duration duration;
final Function onFinal;
final Function onStart;
Footer({
this.activeColor,
this.inactiveColor,
this.currentStep,
this.totalSteps,
this.duration,
this.onFinal,
this.onStart,
}) {}
#override
_FooterState createState() => _FooterState();
}
class _FooterState extends State<Footer> {
final double radius = 10.0;
final double distance = 4.0;
Container stepper;
Container nextArrow;
bool lastPage;
WelcomeBloc _welcomeBloc;
#override
void didChangeDependencies() {
super.didChangeDependencies();
final WelcomeBloc _welcome = Provider.of<WelcomeBloc>(context);
_welcomeBloc = _welcome;
this._detectLastPage();
}
#override
Widget build(BuildContext context) {
this._makeStepper();
this._makeNextArrow();
return Container(
alignment: Alignment.bottomCenter,
padding: EdgeInsets.symmetric(vertical: 30.0, horizontal: 30.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
this.stepper,
this.nextArrow,
],
),
);
}
_makeCirle(activeColor, inactiveColor, position, currentStep) {
currentStep = currentStep == null ? 0 : currentStep - 1;
Color color = (position == currentStep) ? activeColor : inactiveColor;
return Container(
height: this.radius,
width: this.radius,
margin: EdgeInsets.only(left: this.distance, right: this.distance),
decoration: BoxDecoration(
color: color,
border: Border.all(color: activeColor, width: 2.0),
borderRadius: BorderRadius.circular(50.0)),
);
}
_makeStepper() {
List<Container> circles = List();
for (var i = 0; i < widget.totalSteps; i++) {
circles.add(
_makeCirle(this.widget.activeColor, this.widget.inactiveColor, i,
this.widget.currentStep),
);
}
this.stepper = Container(
child: Row(
children: circles,
),
);
}
_makeNextArrow() {
this.nextArrow = Container(
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: GestureDetector(
onTap: () {
_welcomeBloc.controller.nextPage(
duration: this.widget.duration ?? Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
},
child: Icon(
Icons.arrow_forward,
)),
),
);
}
_onLastPage() {
if (this.widget.onFinal != null) {
this.widget.onFinal();
}
}
_onFirstPage() {
if (this.widget.onStart != null) {
this.widget.onStart();
}
}
_detectLastPage() {
// Here I've got inaccurate data
int currentPage =
this.widget.currentStep == null ? 1 : this.widget.currentStep;
if (currentPage == 1 && this.widget.currentStep == null) {
this._onFirstPage();
} else if (currentPage == this.widget.totalSteps) {
print('lastPage detected');
setState(() {
this.lastPage = true;
});
_welcomeBloc.lastPage = true;
this._onLastPage();
} else {
setState(() {
this.lastPage = false;
});
_welcomeBloc.lastPage = false;
}
}
}
P.S.: but I face another problem there with the data accuracy. Inside that hook I could get the class property only one step behind accurate one.
details here: didChangeDependencies hook in Flutter Widget includes not accurate data of the class
I'm trying to create a realtime poseNet demo app with the help of Flutter and its Camera package.
Displaying the camera preview works very well, but when I try to access the image stream via controller.startImageStream the program crashes. I've succeded in tracking the source of that bug down, and it seems to me that the incoming frames in the 'controller.startImageStream' return values of null. Why is this, and how can I possibly fix it?
The snippet looks like this:
controller.startImageStream((CameraImage img) {
controller.stopImageStream();
if (!isPaused) {
runPoseNetOnFrame(bytesList: img.planes.map((plane) {
return plane.bytes;
}).toList(),
imageHeight: img.height,
imageWidth: img.width,
numResults: 1).then((recognitions) {
setState(() {
photo = img;
_recognitions = recognitions;
imageHeight = img.height;
imageWidth = img.width;
});
});
}
});
And it's the variable, img that's for every iteration composed of nothing but type null when it really should contain a frame of type CameraImage
While the app crashes I get this error in my console:
E/flutter (26077): [ERROR:flutter/shell/common/shell.cc(178)] Dart Error: Unhandled exception:
E/flutter (26077): NoSuchMethodError: The getter 'isActive' was called on null.
E/flutter (26077): Receiver: null
E/flutter (26077): Tried calling: isActive
Here's my code for the entire page:
import 'package:flutter/material.dart';
import 'package:tflite/tflite.dart';
import 'package:flutter/services.dart';
import 'dart:typed_data';
import 'package:camera/camera.dart';
class CoachPage extends StatefulWidget {
#override
_CoachPageState createState() {
return _CoachPageState();
}
}
class _CoachPageState extends State<CoachPage> {
bool isPaused = true;
CameraController controller;
List cameras;
int selectedCameraIdx;
String imagePath;
static const MethodChannel _channel = const MethodChannel('tflite');
String model;
List<dynamic> _recognitions;
int imageHeight;
int imageWidth;
CameraImage photo;
#override
void initState() {
super.initState();
availableCameras().then((availableCameras) {
cameras = availableCameras;
if (cameras.length > 0) {
setState(() {
selectedCameraIdx = 0;
});
_initCameraController(cameras[selectedCameraIdx]);
}
else{
print("No camera available");
}
}).catchError((err) {
print('Error: $err.code\nError Message: $err.message');
});
}
void _initCameraController(CameraDescription cameraDescription) {
controller = CameraController(cameraDescription, ResolutionPreset.medium);
controller.initialize().then((_) {
if (!mounted) {
return;
}
controller.addListener(() {});
controller.startImageStream((CameraImage img) {
controller.stopImageStream();
if (!isPaused) {
runPoseNetOnFrame(bytesList: img.planes.map((plane) {
return plane.bytes;
}).toList(),
imageHeight: img.height,
imageWidth: img.width,
numResults: 1).then((recognitions) {
setState(() {
photo = img;
_recognitions = recognitions;
imageHeight = img.height;
imageWidth = img.width;
});
});
}
});
setState(() {});
});}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
flex: 1,
child: isPaused ? _cameraPreviewWidget()
: _renderKeypoints(),
),
SizedBox(height: 10.0),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
_cameraTogglesRowWidget(),
_captureControlRowWidget(context),
Spacer(),
Text(photo.toString())
],
),
SizedBox(height: 20.0)
],
),
),
),
);
}
/// Display Camera preview.
Widget _cameraPreviewWidget() {
if (controller == null || !controller.value.isInitialized) {
return const Text(
'Loading',
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.w900,
),
);
}
return AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: CameraPreview(controller),
);
}
/// Display the control bar with buttons to take pictures
Widget _captureControlRowWidget(context) {
return Expanded(
child: Align(
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: [
FloatingActionButton(
child:(isPaused) ? Icon(Icons.play_arrow)
: Icon(Icons.pause),
backgroundColor: Colors.blueGrey,
onPressed: () {
setState(() {
isPaused = !isPaused;
});
})
],
),
),
);
}
/// Display a row of toggle to select the camera (or a message if no camera is available).
Widget _cameraTogglesRowWidget() {
if (cameras == null || cameras.isEmpty) {
return Spacer();
}
CameraDescription selectedCamera = cameras[selectedCameraIdx];
CameraLensDirection lensDirection = selectedCamera.lensDirection;
return Expanded(
child: Align(
alignment: Alignment.centerLeft,
child: FlatButton.icon(
onPressed: _onSwitchCamera,
icon: Icon(_getCameraLensIcon(lensDirection)),
label: Text(
"${lensDirection.toString().substring(lensDirection.toString().indexOf('.') + 1)}")),
),
);
}
IconData _getCameraLensIcon(CameraLensDirection direction) {
switch (direction) {
case CameraLensDirection.back:
return Icons.camera_rear;
case CameraLensDirection.front:
return Icons.camera_front;
case CameraLensDirection.external:
return Icons.camera;
default:
return Icons.device_unknown;
}
}
void _onSwitchCamera() {
selectedCameraIdx =
selectedCameraIdx < cameras.length - 1 ? selectedCameraIdx + 1 : 0;
CameraDescription selectedCamera = cameras[selectedCameraIdx];
_initCameraController(selectedCamera);
}
void _showCameraException(CameraException e) {
String errorText = 'Error: ${e.code}\nError Message: ${e.description}';
print(errorText);
print('Error: ${e.code}\n${e.description}');
}
Future loadModel() async {
Tflite.close();
String res = await Tflite.loadModel(
model: "assets/posenet_mv1_075_float_from_checkpoints.tflite");
model = res;
}
Future<List> runPoseNetOnFrame(
{#required List<Uint8List> bytesList,
int imageHeight = 1280,
int imageWidth = 720,
double imageMean = 127.5,
double imageStd = 127.5,
int rotation: 90, // Android only
int numResults = 1,
double threshold = 0.5,
int nmsRadius = 20,
bool asynch = true}) async {
return await _channel.invokeMethod(
'runPoseNetOnFrame',
{
"bytesList": bytesList,
"imageHeight": imageHeight,
"imageWidth": imageWidth,
"imageMean": imageMean,
"imageStd": imageStd,
"rotation": rotation,
"numResults": numResults,
"threshold": threshold,
"nmsRadius": nmsRadius,
"asynch": asynch,
},
);
}
List<Widget> _renderKeypoints() {
var lists = <Widget>[];
_recognitions.forEach((re) {
var list = re["keypoints"].values.map<Widget>((k) {
var x = k["x"];
var y = k["y"];
return Positioned(
left: x - 6,
top: y - 6,
width: 100,
height: 12,
child: Container(
child: Text(
"● ${k["part"]}",
style: TextStyle(
color: Color.fromRGBO(37, 213, 253, 1.0),
fontSize: 12.0,
),
),
),
);
}).toList();
lists.addAll(list);
});
return lists;
}
}
Thanks in advance! ;)
Checking your code, the issue seems to be caused by calling controller.stopImageStream(); immediately after controller.startImageStream(). You may want to consider closing the Stream only when it's not in use, like in dispose() override method.