【发布时间】:2018-10-15 20:46:02
【问题描述】:
关于重新渲染,我似乎“失去了泡沫”!我不确定我的应用程序的当前实现有什么问题。它是在一些 SO 成员的帮助下得出的。
它应该做什么:渲染 9 个绿色圆圈,然后依次以递增或递减的顺序将每个圆圈渲染为黄色(圆圈小部件)。显示计数的当前值(计数器小部件)。在 AppBar(Home_Page 小部件)中:识别 + 的点击并增加计数,识别 - 的点击并减少计数。在这两种情况下,都在 setState 方法的主体中执行递增/递减。 Circles 和 Counter 小部件都应该重新渲染。在此实现中,所有圆圈都参与了从绿色到黄色到绿色的颜色变化(实际上忽略了计数)。
它的作用:Circles 和 Counter 小部件的初始渲染按需要显示。但是 AppBar 图标(+ 和 -)虽然可以识别,但不会导致重新渲染 Circles 小部件。 Counter 小部件确实会重新呈现其计数显示。在 Circles 小部件中有一个 RaisedButton,当点击它时会导致重新渲染 Circles 小部件。但该按钮在最终实现中并不需要,仅用于测试。
让我感到困惑的是,用于 Circles 小部件的模板与用于 Counter 小部件的模板相同。然而,它们的执行方式似乎不同。
整个应用程序的源代码如下。它是一个单一的 .dart 文件(抱歉,它太长了,但过去遗漏了一些东西会引起问题)。
想法?
// ignore_for_file: camel_case_types
// ignore_for_file: constant_identifier_names
// ignore_for_file: non_constant_identifier_names
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:math';
const int NUMBER_TILES = 9;
final int CROSS_AXIS_COUNT = (sqrt(NUMBER_TILES)).toInt();
const double CROSS_AXIS_SPACING = 4.0;
const int INITIAL_COUNT = 9; // for testing; should be 1
const double MAIN_AXIS_SPACING = CROSS_AXIS_SPACING;
const int MILLISECOND_MULTIPLIER = 500;
// ************************************************************** main
void main() {
final AppState app_state = new AppState(counter: INITIAL_COUNT);
runApp(new Home_Page(app_state: app_state));
} // main
// **************************************************** class AppState
class AppState {
int counter = 0;
List<int> flash_indices = [];
bool forward = false;
AppState({this.counter}); // AppState
String toString() { // toString
return ( 'AppState{' +
'counter: $counter, ' +
'flash_indices: $flash_indices}');
} // toString
// following is a mock
void randomize_flash_indices ( ) { // randomize_flash_indices
forward = !forward;
if ( forward){
flash_indices = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, -1];
}
else {
flash_indices = [ 8, 7, 6, 5, 4, 3, 2, 1, 0, -1];
}
flash_indices.add (-1); // restore to normal colors
} // randomize_flash_indices
} // class AppState
// *************************************************** class Home_Page
class Home_Page
extends StatefulWidget {
final AppState app_state;
Home_Page({
@required this.app_state,
Key key,
}) : super(key: key);
@override
State<StatefulWidget> createState() {
return Home_Page_State();
}
} // class Home_Page
// ********************************************* class Home_Page_State
class Home_Page_State extends State<Home_Page>{
Home_Page_State();
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Periodic',
theme: new ThemeData(primarySwatch: Colors.indigo),
home: Scaffold(
appBar: AppBar(
title: Text('Periodic'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.add), // increment counter
onPressed: () {
if (widget.app_state.counter < NUMBER_TILES){
setState(() {
widget.app_state.counter++;
});
}
}
),
IconButton(
icon: Icon(Icons.remove), // decrement counter
onPressed: () {
if (widget.app_state.counter > 1){
setState(() {
widget.app_state.counter--;
});
}
}
),
]
),
body: Column(
children: [
Circles (
app_state: widget.app_state,
),
Counter (
app_state: widget.app_state,
)
],
),
),
);
} // Home_Page_State build
} // class Home_Page_State
// ***************************************************** class Circles
class Circles extends StatefulWidget {
final AppState app_state;
Circles({
@required this.app_state,
Key key,
}) : super(key: key);
@override
State<StatefulWidget> createState() {
return Circles_State();
}
} // class Circles
// *********************************************** class Circles_State
class Circles_State extends State<Circles>{
Circles_State();
int flash_tile = -1;
List<GridTile> grid_tiles = <GridTile>[];
StreamController<int> tick_controller;
StreamSubscription<int> tick_listener;
Stream<int> start_ticking() { // start_ticking
tick_controller = new StreamController();
for ( int tick = 0; (tick < widget.app_state.counter); tick++ ) {
Future.delayed(Duration(milliseconds:
MILLISECOND_MULTIPLIER * tick),() {
print('start_ticking() tick: $tick');
tick_controller.add(tick);
});
}
return tick_controller.stream;
} // start_ticking
@override
void initState() { // initState
super.initState();
widget.app_state.randomize_flash_indices();
tick_listener = start_ticking().listen(on_tick);
} // initState
@override
void dispose() { // dispose
if (tick_listener != null) {
tick_listener.cancel();
tick_listener = null;
}
super.dispose();
} // dispose
on_tick(int tick) async { // on_tick
print('listen_for_tick() tick: $tick');
this.setState(() => this.flash_tile =
widget.app_state.
flash_indices[tick]);
} // on_tick
GridTile new_circle_tile( // new_circle_tile
Color tile_color,
int index) {
GridTile tile = GridTile(
child: GestureDetector(
child: Container(
decoration: BoxDecoration(
color: tile_color,
shape: BoxShape.circle,
),
),
)
);
return (tile);
} // new_circle_tile
List<GridTile> create_circle_tiles() {// create_circle_tiles
grid_tiles = new List<GridTile>();
for (int i = 0; (i < NUMBER_TILES); i++) {
Color tile_color =
( this.flash_tile == i) ?
Colors.yellow :
Colors.green;
grid_tiles.add(new_circle_tile(tile_color, i));
}
return (grid_tiles);
} // create_circle_tiles
@override // Circles_State
Widget build(BuildContext context) {
print('Circles_State Build ' +
widget.app_state.toString() +
' flash_tile: $flash_tile');
return Column(
children: [
GridView.count(
shrinkWrap: true,
crossAxisCount: CROSS_AXIS_COUNT,
childAspectRatio: 1.0,
padding: const EdgeInsets.all(4.0),
mainAxisSpacing: MAIN_AXIS_SPACING,
crossAxisSpacing: CROSS_AXIS_SPACING,
children: create_circle_tiles(),
),
RaisedButton(
child: Text("restart"),
onPressed: () {
widget.app_state.randomize_flash_indices();
tick_listener = start_ticking().listen(on_tick);
}
),
] // children
);
} // Circles_State build
} // class Circles_State
// ***************************************************** class Counter
class Counter extends StatefulWidget {
final AppState app_state;
Counter({
@required this.app_state,
Key key,
}) : super(key: key);
@override
State<StatefulWidget> createState() {
return Counter_State();
}
} // class Counter
// *********************************************** class Counter_State
class Counter_State extends State<Counter> {
Counter_State();
@override // Counter_State
Widget build(BuildContext context) {
int counter_value = widget.app_state.counter;
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: SizedBox(
width: 24.0,
child: Center(
child: Text(
'Counter $counter_value',
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
fontSize: 24.0,
),
),
),
),
),
],
);
} // Counter_State build
} // class Counter_State
【问题讨论】:
-
要筛选出很多有用的信息(在我看来)。如果您能清楚地指出问题所在,您就更有可能获得帮助。也就是说,小部件未重新渲染通常是因为在更改
StatefulWidget的属性时缺少对setState()的调用。 -
在 Home_Page_State 中,两个 Icon on_pressed 事件处理程序都在 setState 主体中声明。这就是我认为我会误入歧途的地方。我希望 Circles 和 Counter 小部件都被重新渲染,但只有 Counter 小部件被重新渲染。
-
this.setState(() => this.flash_tile = widget.app_state.flash_indices[tick]);这是你设置圈子状态的地方吗? -
@JacobPhilips 我认为我正在设置 Circles 状态的三个位置:一个在 Circles_State 类的 on_tick 方法中(您已确定),两个在 Home_Page_State AppBar 操作中,其中我编写了“setState( (){widget.app_state.counter++;});"用于计数器的增量和“setState((){widget.app_state.counter--;});”因为它的减量。请注意,最后两个不在 Circles_State 类中,而是通过 AppState 类中的变量 counter 传达更改。