首先,我要说的是,我不确定是否应该在stackoverflow上问这个问题,但是除了社区,我没有人要问这个问题,因为我们是在小型创业公司中工作,除了两个人之外,其他开发人员都没有动静我们。
我(大约一年的开发时间,大约一半的时间研究flutter)和一种“具有10年经验的高级移动开发人员”进行了激烈的讨论。讨论的主题是在无状态小部件中使用非最终字段。他正在做,他正在编写这样的代码。他说这是解决他的问题的最好方法。我是说这不是一个好主意,他要么需要有状态的小部件,要么他的设计很糟糕,并且他不需要非最终字段。
所以我的问题是:是否存在一种情况,在无状态小部件中使用非最终字段是合理的?
他的论点:
我知道前两个参数很愚蠢,只有第四个参数值得讨论。
这个问题的可能重复也不能说服我的同事。 Flutter: Mutable fields in stateless widgets
请查看他的代码:
namespace PizzaCreator
{
class Program
{
static void Main (string[] args)
{
var done = false;
do
{
switch (DisplayMenu())
{
case Command.New: NewOrder(); break;
case Command.Display: DisplayOrder(); break;
case Command.Quit: done = true; break;
}
} while (!done);
}
enum Command
{
Quit = 0,
Display = 1,
New = 2,
}
static string size;
static string meat;
static string vegtables;
static string sauce;
static bool cheese;
static bool delivery;
private static Command DisplayMenu ()
{
do
{
Console.WriteLine("N)ew Order: ");
Console.WriteLine("D)isplay Order: ");
Console.WriteLine("Q)uit.");
var input = Console.ReadLine();
switch (input.ToLower())
{
case "n": return Command.New;
case "d": return Command.Display;
case "q": return Command.Quit;
default: Console.WriteLine("Invalid option, please enter a valid letter. "); break;
};
} while (true);
}
private static bool ReadBoolean (string message)
{
Console.WriteLine(message + " (Y/N) ");
do
{
string value = Console.ReadLine();
if (!String.IsNullOrEmpty(value))
{
if (String.Compare(value, "Y", true) == 0)
return true;
else if (String.Compare(value, "N", true) == 0)
return false;
char firstChar = value[0];
};
Console.WriteLine("Enter Y/N: ");
} while (true);
}
static void NewOrder()
{
Console.WriteLine(Size());
Console.WriteLine(Sauce());
Console.WriteLine(Cheese());
Console.WriteLine("Meat: ");
Console.WriteLine("Vegetables: ");
Console.WriteLine("Delivery: (choose one)");
}
private static bool Size ()
{
Console.WriteLine("Pizza size:\n S)mall(5.00)\n M)eduium(6.25)\n L)arge(8.75)");
Console.Write("Choose one: ");
var choice = Console.ReadLine();
double cost = 0;
switch (choice)
{
case "S":
cost += 5.00;
break;
case "M":
cost += 6.25;
break;
case "L":
cost += 8.75;
break;
default:
Console.WriteLine("Invalid Selection please select S, M, or L");
break;
}
return (true);
}
private static bool Sauce ()
{
Console.WriteLine("Type of Sauce:\n T)raditional(0.00)\n G)arlic(1.00)\n O)regano(1.00)");
Console.Write("Choose one: ");
var choice = Console.ReadLine();
double cost = 0;
switch (choice)
{
case "T":
cost += 0.00;
break;
case "G":
cost += 1.00;
break;
case "O":
cost += 1.00;
break;
default:
Console.WriteLine("Invalid Selection please select T, G, or O");
break;
}
return true;
}
private static bool Cheese()
{
double cost = 0;
Console.WriteLine("R)eagular($0.00)\nE)xtra($1.25) ");
do
{
string value = Console.ReadLine();
if (!String.IsNullOrEmpty(value))
{
if (String.Compare(value, "R", true) == 0)
return true;
else if (String.Compare(value, "E", true) == 0)
cost += 1.25;
return false;
char firstChar = value[0];
};
Console.WriteLine("Enter R/E ");
} while (true);
}
static void Meat()
{
}
static void Vegtables()
{
}
static bool Delivery()
{
double cost = 0;
Console.WriteLine("T)akeout($0.00)\nD)elivery($1.25) ");
do
{
string value = Console.ReadLine();
if (!String.IsNullOrEmpty(value))
{
if (String.Compare(value, "T", true) == 0)
return true;
else if (String.Compare(value, "D", true) == 0)
cost += 2.50;
return false;
char firstChar = value[0];
};
Console.WriteLine("Enter T/D ");
} while (true);
}
private static void DisplayOrder()
{
if (String.IsNullOrEmpty(size))
{
Console.WriteLine("No Pizza Selected");
return;
}
Console.WriteLine(size);
Console.WriteLine(sauce);
Console.WriteLine(cheese ? "Cheese" : "No cheese");
Console.WriteLine(meat);
Console.WriteLine(vegtables);
Console.WriteLine(delivery ? "Delivery" : "Take out");
}
}
}
答案 0 :(得分:1)
免责声明:我不擅长解释,希望您能从阅读此垃圾内容中得到一些帮助。我什至不认为这可以称为解释
class GameDiscussThePicture extends StatelessWidget {
GameDiscussThePicture();
/// As he said, BLoC is the one holding state, therefore if he wants a non final field
/// declare it in ChatBloc not here.
/// class ChatBloc extends Bloc {
/// CarouselSlider _slider;
/// CarouselSlider get slider => _slider;
/// }
/// To access it, BlocProvider.of<ChatBloc>(context).slider;
CarouselSlider _slider;
@override
Widget build(BuildContext context) {
return BlocBuilder(
bloc: BlocProvider.of<ChatBloc>(context),
condition: (previousState, state) {
return previousState != GameClosed();
},
builder: (context, state) {
if (state is GameDiscussTopicChanged) {
_showPictureWith(context, state.themeIndex);
} else if (state is GameClosed) {
Navigator.of(context).pop();
return Container();
}
final _chatBloc = BlocProvider.of<ChatBloc>(context);
return Scaffold(
appBar: AppBar(
backgroundColor: Color.fromARGB(255, 255, 255, 255),
leading: BackButton(
color: Color.fromARGB(255, 12, 12, 13),
/// he can just write _chatBloc.add(GameCancel()) here.
onPressed: () => BlocProvider.of<ChatBloc>(context).add(GameCancel()),
),
),
//SafeArea
body: DecoratedBox(
decoration: BoxDecoration(color: Color.fromARGB(255, 240, 240, 240)),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 15),
_carouselSlider(context),
Container(
height: 88,
child: DecoratedBox(
decoration: BoxDecoration(color: Color.fromARGB(255, 255, 255, 255)),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
if (_chatBloc.partnerAvatar() != null) Image.network(_chatBloc.partnerAvatar(), fit: BoxFit.cover, width: 75.0),
if (_chatBloc.partnerAvatar() == null) Text('RU', style: TextStyle(fontSize: 22)),
Padding(
padding: EdgeInsets.fromLTRB(20, 0, 20, 0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(_chatBloc.partnerName(), style: TextStyle(fontSize: 20, fontWeight: FontWeight.normal),),
ChatStopwatch(),
// Text('До конца 06:33', style: TextStyle(fontSize: 14, fontWeight: FontWeight.normal),),
],
)
),
// FlatButton(
// child: Image.asset('assets/images/mic_off.png', width: 30, height: 30,),
// onPressed: () => print('mic off pressed'),
// ),
FlatButton(
child: Image.asset('assets/images/hang_off.png', width: 60, height: 60,),
onPressed: () => ChatHelper.confirmEndingDialog(context)
),
]),
))
],
),
),
],
),
),
);
});
}
@widget
/// Also rather than passing BuildContext, passing the _chatBloc is better.
/// I am not sure why, but I've read somewhere BuildContext is not meant to be passed
/// around. And you don't need to make another final field for BlocProvider.of<ChatBloc>
/// (context)
/// Widget _carouselSlider(ChatBloc chatBloc) {
/// and here you can do something like chatBloc.slider = CarouselSlider(); in case
/// that slider field will be used again somehow.
/// }
/// Tho just return CarouselSlider instead is better in this scenario IMO.
Widget _carouselSlider(BuildContext context) {
final chatBloc = BlocProvider.of<ChatBloc>(context);
_slider = CarouselSlider(
height: 600.0,
viewportFraction: 0.9,
reverse: false,
enableInfiniteScroll: false,
initialPage: chatBloc.gameDiscussCurrentIdx,
onPageChanged: (index) {
final chatBloc = BlocProvider.of<ChatBloc>(context);
if (chatBloc.gameDiscussCurrentIdx < index) {
chatBloc.add(GameDiscussTopicChange(themeIndex: index));
} else {
_slider.animateToPage(chatBloc.gameDiscussCurrentIdx, duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
}
},
items: chatBloc.gameDiscussPictures.map((item) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.symmetric(horizontal: 5.0),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(item.titleEn, style: Styles.h3),
SizedBox(height: 15.0,),
ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
child: Image.network(item.getImageUrl(), fit: BoxFit.cover, width: MediaQuery.of(context).size.width),
)
]
),
);
},
);
}).toList(),
);
return _slider;
}
_onPictureChanged(BuildContext context, int index) {
final chatBloc = BlocProvider.of<ChatBloc>(context);
if (chatBloc.gameDiscussCurrentIdx < index) {
chatBloc.add(GameDiscussTopicChange(themeIndex: index));
} else {
_slider.animateToPage(chatBloc.gameDiscussCurrentIdx, duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
}
}
_showPictureWith(BuildContext context, int index) {
final chatBloc = BlocProvider.of<ChatBloc>(context);
chatBloc.gameDiscussCurrentIdx = index;
_slider.animateToPage(chatBloc.gameDiscussCurrentIdx, duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
}
}